yamadashy/repomix/main 530k tokens More Tools
```
├── .clinerules
├── .codecov.yml
├── .cursorrules
├── .devcontainer/
   ├── devcontainer.json
├── .dockerignore
├── .editorconfig (omitted)
├── .github/
   ├── CODEOWNERS
   ├── FUNDING.yml
   ├── ISSUE_TEMPLATE/
      ├── 01_feature_request.yml (100 tokens)
      ├── 02_bug_report.yml (200 tokens)
      ├── config.yml
   ├── actions/
      ├── repomix/
         ├── action.yml (600 tokens)
   ├── dependabot.yml
   ├── instructions/
      ├── base.instructions.md (1000 tokens)
      ├── github-release-note.instructions.md (1300 tokens)
      ├── website.instructions.md (400 tokens)
   ├── pull_request_template.md
   ├── renovate.json5 (100 tokens)
   ├── workflows/
      ├── ci.yml (700 tokens)
      ├── claude.yml (200 tokens)
      ├── codeql.yml (300 tokens)
      ├── docker.yml (400 tokens)
      ├── homebrew.yml (100 tokens)
      ├── pack-repository.yml (100 tokens)
      ├── test-action.yml (300 tokens)
├── .gitignore (100 tokens)
├── .node-version
├── .npmignore (100 tokens)
├── .repomixignore
├── .secretlintrc.json
├── .tool-versions
├── CLAUDE.md (300 tokens)
├── CODE_OF_CONDUCT.md (700 tokens)
├── CONTRIBUTING.md (600 tokens)
├── Dockerfile (100 tokens)
├── LICENSE (omitted)
├── README.md (10k tokens)
├── SECURITY.md
├── bin/
   ├── repomix.cjs (300 tokens)
├── biome.json (200 tokens)
├── browser/
   ├── .gitignore (100 tokens)
   ├── README.md (300 tokens)
   ├── app/
      ├── _locales/
         ├── de/
            ├── detailed-description.txt (300 tokens)
            ├── messages.json (100 tokens)
         ├── en/
            ├── detailed-description.txt (300 tokens)
            ├── messages.json (100 tokens)
         ├── es/
            ├── detailed-description.txt (300 tokens)
            ├── messages.json (100 tokens)
         ├── fr/
            ├── detailed-description.txt (300 tokens)
            ├── messages.json (100 tokens)
         ├── ja/
            ├── detailed-description.txt (100 tokens)
            ├── messages.json (100 tokens)
         ├── ko/
            ├── detailed-description.txt (200 tokens)
            ├── messages.json (100 tokens)
         ├── pt_BR/
            ├── detailed-description.txt (300 tokens)
            ├── messages.json (100 tokens)
         ├── zh_CN/
            ├── detailed-description.txt (100 tokens)
            ├── messages.json (100 tokens)
         ├── zh_TW/
            ├── detailed-description.txt (100 tokens)
            ├── messages.json (100 tokens)
      ├── images/
         ├── icon-128.png
         ├── icon-16.png
         ├── icon-19.png
         ├── icon-32.png
         ├── icon-38.png
         ├── icon-48.png
         ├── icon-64.png
         ├── icon.svg (200 tokens)
      ├── manifest.json (300 tokens)
      ├── scripts/
         ├── background.ts (200 tokens)
         ├── content.ts (800 tokens)
      ├── styles/
         ├── content.css
   ├── package-lock.json (71.7k tokens)
   ├── package.json (200 tokens)
   ├── promo/
      ├── Chrome-Webstore-Icon_128x128.png
      ├── Promo-Image-Marquee_1400x560.png
      ├── Promo-Image-Small_440x280.png
      ├── Screenshot_1280x800.png
   ├── scripts/
      ├── generate-icons.ts (400 tokens)
   ├── tests/
      ├── repomix-integration.test.ts (300 tokens)
   ├── tsconfig.json (100 tokens)
   ├── vitest.config.ts
├── llms-install.md (2k tokens)
├── package-lock.json (omitted)
├── package.json (700 tokens)
├── repomix-instruction.md (2.1k tokens)
├── repomix.config.json (300 tokens)
├── src/
   ├── cli/
      ├── actions/
         ├── defaultAction.ts (1400 tokens)
         ├── initAction.ts (1100 tokens)
         ├── mcpAction.ts
         ├── migrationAction.ts (2000 tokens)
         ├── remoteAction.ts (800 tokens)
         ├── versionAction.ts
      ├── cliPrint.ts (1000 tokens)
      ├── cliRun.ts (1600 tokens)
      ├── cliSpinner.ts (300 tokens)
      ├── types.ts (200 tokens)
   ├── config/
      ├── configLoad.ts (900 tokens)
      ├── configSchema.ts (1000 tokens)
      ├── defaultIgnore.ts (600 tokens)
      ├── globalDirectory.ts (100 tokens)
   ├── core/
      ├── file/
         ├── fileCollect.ts (400 tokens)
         ├── fileManipulate.ts (1500 tokens)
         ├── filePathSort.ts (200 tokens)
         ├── fileProcess.ts (400 tokens)
         ├── fileProcessContent.ts (500 tokens)
         ├── fileRead.ts (300 tokens)
         ├── fileSearch.ts (1800 tokens)
         ├── fileTreeGenerate.ts (400 tokens)
         ├── fileTypes.ts
         ├── packageJsonParse.ts (200 tokens)
         ├── permissionCheck.ts (600 tokens)
         ├── workers/
            ├── fileCollectWorker.ts (100 tokens)
            ├── fileProcessWorker.ts (100 tokens)
      ├── git/
         ├── gitCommand.ts (1000 tokens)
         ├── gitDiffHandle.ts (500 tokens)
         ├── gitRemoteHandle.ts (200 tokens)
         ├── gitRemoteParse.ts (400 tokens)
         ├── gitRepositoryHandle.ts (300 tokens)
      ├── metrics/
         ├── TokenCounter.ts (200 tokens)
         ├── calculateAllFileMetrics.ts (400 tokens)
         ├── calculateMetrics.ts (500 tokens)
         ├── calculateOutputMetrics.ts (400 tokens)
         ├── workers/
            ├── fileMetricsWorker.ts (300 tokens)
            ├── outputMetricsWorker.ts (200 tokens)
            ├── types.ts
      ├── output/
         ├── outputGenerate.ts (1600 tokens)
         ├── outputGeneratorTypes.ts (200 tokens)
         ├── outputSort.ts (400 tokens)
         ├── outputStyleDecorate.ts (1300 tokens)
         ├── outputStyles/
            ├── markdownStyle.ts (600 tokens)
            ├── plainStyle.ts (300 tokens)
            ├── xmlStyle.ts (200 tokens)
      ├── packager.ts (800 tokens)
      ├── packager/
         ├── copyToClipboardIfEnabled.ts (300 tokens)
         ├── writeOutputToDisk.ts (200 tokens)
      ├── security/
         ├── filterOutUntrustedFiles.ts (100 tokens)
         ├── securityCheck.ts (600 tokens)
         ├── validateFileSafety.ts (400 tokens)
         ├── workers/
            ├── securityCheckWorker.ts (400 tokens)
      ├── treeSitter/
         ├── ext2Lang.ts (100 tokens)
         ├── lang2Query.ts (200 tokens)
         ├── languageParser.ts (600 tokens)
         ├── loadLanguage.ts (200 tokens)
         ├── parseFile.ts (800 tokens)
         ├── parseStrategies/
            ├── CssParseStrategy.ts (300 tokens)
            ├── DefaultParseStrategy.ts (200 tokens)
            ├── GoParseStrategy.ts (1300 tokens)
            ├── ParseStrategy.ts (300 tokens)
            ├── PythonParseStrategy.ts (900 tokens)
            ├── TypeScriptParseStrategy.ts (1000 tokens)
            ├── VueParseStrategy.ts (200 tokens)
         ├── queries/
            ├── README.md (500 tokens)
            ├── queryC.ts (100 tokens)
            ├── queryCSharp.ts (200 tokens)
            ├── queryCpp.ts (200 tokens)
            ├── queryCss.ts
            ├── queryGo.ts (300 tokens)
            ├── queryJava.ts (200 tokens)
            ├── queryJavascript.ts (500 tokens)
            ├── queryPhp.ts (200 tokens)
            ├── queryPython.ts (200 tokens)
            ├── queryRuby.ts (300 tokens)
            ├── queryRust.ts (400 tokens)
            ├── querySolidity.ts (100 tokens)
            ├── querySwift.ts (200 tokens)
            ├── queryTypescript.ts (400 tokens)
            ├── queryVue.ts
   ├── index.ts (500 tokens)
   ├── mcp/
      ├── mcpServer.ts (500 tokens)
      ├── prompts/
         ├── packRemoteRepositoryPrompts.ts (400 tokens)
      ├── tools/
         ├── fileSystemReadDirectoryTool.ts (500 tokens)
         ├── fileSystemReadFileTool.ts (600 tokens)
         ├── grepRepomixOutputTool.ts (1400 tokens)
         ├── mcpToolRuntime.ts (1200 tokens)
         ├── packCodebaseTool.ts (700 tokens)
         ├── packRemoteRepositoryTool.ts (700 tokens)
         ├── readRepomixOutputTool.ts (700 tokens)
   ├── shared/
      ├── constants.ts
      ├── errorHandle.ts (400 tokens)
      ├── logger.ts (500 tokens)
      ├── patternUtils.ts (200 tokens)
      ├── processConcurrency.ts (200 tokens)
      ├── types.ts
   ├── types/
      ├── git-url-parse.d.ts (omitted)
├── tests/
   ├── cli/
      ├── actions/
         ├── defaultAction.test.ts (3k tokens)
         ├── diffsFlag.test.ts (200 tokens)
         ├── initAction.test.ts (1200 tokens)
         ├── mcpAction.test.ts (200 tokens)
         ├── migrationAction.test.ts (1600 tokens)
         ├── remoteAction.test.ts (600 tokens)
         ├── versionAction.test.ts (200 tokens)
      ├── cliPrint.test.ts (1200 tokens)
      ├── cliRun.test.ts (2.4k tokens)
   ├── config/
      ├── configLoad.test.ts (1300 tokens)
      ├── configSchema.test.ts (1300 tokens)
      ├── globalDirectory.test.ts (600 tokens)
   ├── core/
      ├── file/
         ├── fileCollect.test.ts (1300 tokens)
         ├── fileManipulate.test.ts (3.4k tokens)
         ├── filePathSort.test.ts (500 tokens)
         ├── fileProcess.test.ts (1100 tokens)
         ├── fileProcessContent.test.ts (1100 tokens)
         ├── fileSearch.test.ts (3k tokens)
         ├── packageJsonParse.test.ts (300 tokens)
         ├── permissionCheck.test.ts (1600 tokens)
      ├── git/
         ├── gitCommand.test.ts (2.3k tokens)
         ├── gitDiffHandle.test.ts (1300 tokens)
         ├── gitRemoteHandle.test.ts (700 tokens)
         ├── gitRemoteParse.test.ts (1100 tokens)
         ├── gitRepositoryHandle.test.ts (800 tokens)
      ├── metrics/
         ├── TokenCounter.test.ts (900 tokens)
         ├── calculateAllFileMetrics.test.ts (300 tokens)
         ├── calculateMetrics.test.ts (500 tokens)
         ├── calculateOutputMetrics.test.ts (1000 tokens)
         ├── diffTokenCount.test.ts (1500 tokens)
      ├── output/
         ├── diffsInOutput.test.ts (1400 tokens)
         ├── outputGenerate.test.ts (1400 tokens)
         ├── outputGenerateDiffs.test.ts (1500 tokens)
         ├── outputSort.test.ts (800 tokens)
         ├── outputStyleDecorate.test.ts (1000 tokens)
         ├── outputStyles/
            ├── markdownStyle.test.ts (1900 tokens)
            ├── plainStyle.test.ts (300 tokens)
            ├── xmlStyle.test.ts (300 tokens)
      ├── packager.test.ts (800 tokens)
      ├── packager/
         ├── copyToClipboardIfEnabled.test.ts (900 tokens)
         ├── diffsFunctionality.test.ts (1000 tokens)
         ├── writeOutputToDisk.test.ts (300 tokens)
      ├── security/
         ├── filterOutUntrustedFiles.test.ts (300 tokens)
         ├── securityCheck.test.ts (1600 tokens)
         ├── validateFileSafety.test.ts (400 tokens)
         ├── workers/
            ├── securityCheckWorker.test.ts (500 tokens)
      ├── treeSitter/
         ├── LanguageParser.test.ts (200 tokens)
         ├── loadLanguage.test.ts (400 tokens)
         ├── parseFile.c.test.ts (1000 tokens)
         ├── parseFile.comments.test.ts (1300 tokens)
         ├── parseFile.cpp.test.ts (600 tokens)
         ├── parseFile.csharp.test.ts (300 tokens)
         ├── parseFile.css.test.ts (1100 tokens)
         ├── parseFile.go.test.ts (900 tokens)
         ├── parseFile.java.test.ts (300 tokens)
         ├── parseFile.javascript.test.ts (400 tokens)
         ├── parseFile.php.test.ts (400 tokens)
         ├── parseFile.python.test.ts (700 tokens)
         ├── parseFile.ruby.test.ts (1100 tokens)
         ├── parseFile.rust.test.ts (400 tokens)
         ├── parseFile.solidity.test.ts (500 tokens)
         ├── parseFile.swift.test.ts (1500 tokens)
         ├── parseFile.test.ts (300 tokens)
         ├── parseFile.typescript.test.ts (3.3k tokens)
         ├── parseFile.vue.test.ts (800 tokens)
   ├── integration-tests/
      ├── fixtures/
         ├── packager/
            ├── inputs/
               ├── simple-project/
                  ├── .repomixignore
                  ├── README.md
                  ├── build/
                     ├── test.js
                  ├── package.json (100 tokens)
                  ├── repomix.config.json (100 tokens)
                  ├── resources/
                     ├── .repomixignore
                     ├── data.txt
                     ├── ignored-data.txt
                  ├── src/
                     ├── build/
                        ├── test.js
                     ├── index.js
                     ├── utils.js
            ├── outputs/
               ├── simple-project-output.md (600 tokens)
               ├── simple-project-output.txt (800 tokens)
               ├── simple-project-output.xml (700 tokens)
      ├── packager.test.ts (1700 tokens)
   ├── mcp/
      ├── mcpServer.test.ts (1300 tokens)
      ├── prompts/
         ├── packRemoteRepositoryPrompts.test.ts (600 tokens)
      ├── tools/
         ├── fileSystemReadDirectoryTool.test.ts (500 tokens)
         ├── fileSystemReadFileTool.test.ts (500 tokens)
         ├── grepRepomixOutputTool.test.ts (5.5k tokens)
         ├── mcpToolRuntime.test.ts (1800 tokens)
         ├── packCodebaseTool.test.ts (1200 tokens)
         ├── readRepomixOutputTool.test.ts (1400 tokens)
   ├── shared/
      ├── logger.test.ts (700 tokens)
      ├── patternUtils.test.ts (400 tokens)
      ├── processConcurrency.test.ts (500 tokens)
   ├── testing/
      ├── testUtils.ts (300 tokens)
├── tsconfig.build.json
├── tsconfig.json (100 tokens)
├── typos.toml (100 tokens)
├── vitest.config.ts (100 tokens)
├── website/
   ├── README.md (100 tokens)
   ├── client/
      ├── .gitignore
      ├── .tool-versions
      ├── .vitepress/
         ├── config.ts (300 tokens)
         ├── config/
            ├── configDe.ts (600 tokens)
            ├── configEnUs.ts (400 tokens)
            ├── configEs.ts (500 tokens)
            ├── configFr.ts (600 tokens)
            ├── configHi.ts (500 tokens)
            ├── configId.ts (500 tokens)
            ├── configJa.ts (500 tokens)
            ├── configKo.ts (500 tokens)
            ├── configPtBr.ts (600 tokens)
            ├── configShard.ts (1100 tokens)
            ├── configVi.ts (500 tokens)
            ├── configZhCn.ts (500 tokens)
            ├── configZhTw.ts (500 tokens)
         ├── theme/
            ├── component.d.ts (omitted)
            ├── custom.css (200 tokens)
            ├── index.ts (100 tokens)
            ├── style.css (900 tokens)
      ├── Dockerfile
      ├── components/
         ├── Home.vue
         ├── Home/
            ├── Hero.vue (200 tokens)
            ├── PackButton.vue (300 tokens)
            ├── TryIt.vue (1700 tokens)
            ├── TryItFileUpload.vue (900 tokens)
            ├── TryItFolderUpload.vue (1700 tokens)
            ├── TryItPackOptions.vue (2.3k tokens)
            ├── TryItResult.vue (500 tokens)
            ├── TryItResultContent.vue (2k tokens)
            ├── TryItResultErrorContent.vue (600 tokens)
            ├── TryItUrlInput.vue (900 tokens)
         ├── HomeBadges.vue (300 tokens)
         ├── api/
            ├── client.ts (300 tokens)
         ├── utils/
            ├── analytics.ts (800 tokens)
            ├── requestHandlers.ts (400 tokens)
            ├── resultViewer.ts (700 tokens)
            ├── validation.ts (100 tokens)
      ├── package-lock.json (53.8k tokens)
      ├── package.json (100 tokens)
      ├── scripts/
         ├── generateSchema.ts (400 tokens)
      ├── src/
         ├── de/
            ├── guide/
               ├── code-compress.md (500 tokens)
               ├── command-line-options.md (700 tokens)
               ├── comment-removal.md (300 tokens)
               ├── configuration.md (2.3k tokens)
               ├── custom-instructions.md (300 tokens)
               ├── development/
                  ├── index.md (800 tokens)
                  ├── using-repomix-as-a-library.md (500 tokens)
               ├── github-actions.md (800 tokens)
               ├── index.md (500 tokens)
               ├── installation.md (400 tokens)
               ├── mcp-server.md (2.4k tokens)
               ├── output.md (300 tokens)
               ├── prompt-examples.md (700 tokens)
               ├── remote-repository-processing.md (300 tokens)
               ├── security.md (300 tokens)
               ├── tips/
                  ├── best-practices.md (700 tokens)
               ├── usage.md (300 tokens)
            ├── index.md (1100 tokens)
         ├── en/
            ├── guide/
               ├── code-compress.md (500 tokens)
               ├── command-line-options.md (700 tokens)
               ├── comment-removal.md (200 tokens)
               ├── configuration.md (2.2k tokens)
               ├── custom-instructions.md (300 tokens)
               ├── development/
                  ├── index.md (800 tokens)
                  ├── using-repomix-as-a-library.md (500 tokens)
               ├── github-actions.md (700 tokens)
               ├── index.md (400 tokens)
               ├── installation.md (400 tokens)
               ├── mcp-server.md (2.2k tokens)
               ├── output.md (400 tokens)
               ├── prompt-examples.md (600 tokens)
               ├── remote-repository-processing.md (300 tokens)
               ├── security.md (300 tokens)
               ├── tips/
                  ├── best-practices.md (700 tokens)
               ├── usage.md (300 tokens)
            ├── index.md (1000 tokens)
         ├── es/
            ├── guide/
               ├── code-compress.md (500 tokens)
               ├── command-line-options.md (800 tokens)
               ├── comment-removal.md (300 tokens)
               ├── configuration.md (2.4k tokens)
               ├── custom-instructions.md (300 tokens)
               ├── development/
                  ├── index.md (800 tokens)
                  ├── using-repomix-as-a-library.md (500 tokens)
               ├── github-actions.md (700 tokens)
               ├── index.md (500 tokens)
               ├── installation.md (400 tokens)
               ├── mcp-server.md (2.4k tokens)
               ├── output.md (400 tokens)
               ├── prompt-examples.md (700 tokens)
               ├── remote-repository-processing.md (300 tokens)
               ├── security.md (300 tokens)
               ├── tips/
                  ├── best-practices.md (800 tokens)
               ├── usage.md (300 tokens)
            ├── index.md (1100 tokens)
         ├── fr/
            ├── guide/
               ├── code-compress.md (600 tokens)
               ├── command-line-options.md (800 tokens)
               ├── comment-removal.md (300 tokens)
               ├── configuration.md (2.4k tokens)
               ├── custom-instructions.md (300 tokens)
               ├── development/
                  ├── index.md (800 tokens)
                  ├── using-repomix-as-a-library.md (500 tokens)
               ├── github-actions.md (700 tokens)
               ├── index.md (500 tokens)
               ├── installation.md (400 tokens)
               ├── mcp-server.md (2.5k tokens)
               ├── output.md (400 tokens)
               ├── prompt-examples.md (700 tokens)
               ├── remote-repository-processing.md (300 tokens)
               ├── security.md (300 tokens)
               ├── tips/
                  ├── best-practices.md (800 tokens)
               ├── usage.md (300 tokens)
            ├── index.md (1100 tokens)
         ├── hi/
            ├── guide/
               ├── code-compress.md (700 tokens)
               ├── command-line-options.md (800 tokens)
               ├── comment-removal.md (800 tokens)
               ├── configuration.md (800 tokens)
               ├── custom-instructions.md (800 tokens)
               ├── development/
                  ├── index.md (400 tokens)
                  ├── using-repomix-as-a-library.md (900 tokens)
               ├── github-actions.md (1000 tokens)
               ├── index.md (500 tokens)
               ├── installation.md (300 tokens)
               ├── mcp-server.md (600 tokens)
               ├── output.md (800 tokens)
               ├── prompt-examples.md (800 tokens)
               ├── remote-repository-processing.md (700 tokens)
               ├── security.md (500 tokens)
               ├── tips/
                  ├── best-practices.md (1000 tokens)
               ├── usage.md (400 tokens)
            ├── index.md (1100 tokens)
         ├── id/
            ├── guide/
               ├── code-compress.md (500 tokens)
               ├── command-line-options.md (500 tokens)
               ├── comment-removal.md (300 tokens)
               ├── configuration.md (600 tokens)
               ├── custom-instructions.md (300 tokens)
               ├── development/
                  ├── index.md (700 tokens)
                  ├── using-repomix-as-a-library.md (1100 tokens)
               ├── github-actions.md (600 tokens)
               ├── index.md (500 tokens)
               ├── installation.md (300 tokens)
               ├── mcp-server.md (700 tokens)
               ├── output.md (400 tokens)
               ├── prompt-examples.md (600 tokens)
               ├── remote-repository-processing.md (400 tokens)
               ├── security.md (500 tokens)
               ├── tips/
                  ├── best-practices.md (800 tokens)
               ├── usage.md (500 tokens)
            ├── index.md (1100 tokens)
         ├── ja/
            ├── guide/
               ├── code-compress.md (300 tokens)
               ├── command-line-options.md (500 tokens)
               ├── comment-removal.md (200 tokens)
               ├── configuration.md (1600 tokens)
               ├── custom-instructions.md (200 tokens)
               ├── development/
                  ├── index.md (600 tokens)
                  ├── using-repomix-as-a-library.md (400 tokens)
               ├── github-actions.md (600 tokens)
               ├── index.md (300 tokens)
               ├── installation.md (300 tokens)
               ├── mcp-server.md (1400 tokens)
               ├── output.md (300 tokens)
               ├── prompt-examples.md (300 tokens)
               ├── remote-repository-processing.md (200 tokens)
               ├── security.md (200 tokens)
               ├── tips/
                  ├── best-practices.md (300 tokens)
               ├── usage.md (200 tokens)
            ├── index.md (700 tokens)
         ├── ko/
            ├── guide/
               ├── code-compress.md (300 tokens)
               ├── command-line-options.md (500 tokens)
               ├── comment-removal.md (200 tokens)
               ├── configuration.md (1700 tokens)
               ├── custom-instructions.md (300 tokens)
               ├── development/
                  ├── index.md (500 tokens)
                  ├── using-repomix-as-a-library.md (400 tokens)
               ├── github-actions.md (600 tokens)
               ├── index.md (300 tokens)
               ├── installation.md (300 tokens)
               ├── mcp-server.md (1400 tokens)
               ├── output.md (300 tokens)
               ├── prompt-examples.md (300 tokens)
               ├── remote-repository-processing.md (200 tokens)
               ├── security.md (200 tokens)
               ├── tips/
                  ├── best-practices.md (300 tokens)
               ├── usage.md (200 tokens)
            ├── index.md (700 tokens)
         ├── pt-br/
            ├── guide/
               ├── code-compress.md (500 tokens)
               ├── command-line-options.md (700 tokens)
               ├── comment-removal.md (200 tokens)
               ├── configuration.md (2.3k tokens)
               ├── custom-instructions.md (300 tokens)
               ├── development/
                  ├── index.md (800 tokens)
                  ├── using-repomix-as-a-library.md (500 tokens)
               ├── github-actions.md (700 tokens)
               ├── index.md (500 tokens)
               ├── installation.md (400 tokens)
               ├── mcp-server.md (2.4k tokens)
               ├── output.md (400 tokens)
               ├── prompt-examples.md (700 tokens)
               ├── remote-repository-processing.md (300 tokens)
               ├── security.md (300 tokens)
               ├── tips/
                  ├── best-practices.md (600 tokens)
               ├── usage.md (300 tokens)
            ├── index.md (1100 tokens)
         ├── public/
            ├── images/
               ├── docs/
                  ├── browser-extension.png
                  ├── repomix-file-usage-1.png
                  ├── repomix-file-usage-2.png
               ├── og-image-large.png
               ├── pwa/
                  ├── repomix-192x192.png
                  ├── repomix-512x512.png
               ├── repomix-logo.png
               ├── repomix-logo.svg (200 tokens)
               ├── repomix-title.png
               ├── sponsors/
                  ├── warp/
                     ├── Terminal-Image.png
            ├── schemas/
               ├── 0.3.5/
                  ├── schema.json (600 tokens)
               ├── latest/
                  ├── schema.json (600 tokens)
         ├── vi/
            ├── guide/
               ├── code-compress.md (900 tokens)
               ├── command-line-options.md (500 tokens)
               ├── comment-removal.md (700 tokens)
               ├── configuration.md (800 tokens)
               ├── custom-instructions.md (700 tokens)
               ├── development/
                  ├── index.md (900 tokens)
                  ├── using-repomix-as-a-library.md (1400 tokens)
               ├── github-actions.md (1000 tokens)
               ├── index.md (500 tokens)
               ├── installation.md (400 tokens)
               ├── mcp-server.md (800 tokens)
               ├── output.md (900 tokens)
               ├── prompt-examples.md (900 tokens)
               ├── remote-repository-processing.md (700 tokens)
               ├── security.md (700 tokens)
               ├── tips/
                  ├── best-practices.md (1100 tokens)
               ├── usage.md (800 tokens)
            ├── index.md (1000 tokens)
         ├── zh-cn/
            ├── guide/
               ├── code-compress.md (300 tokens)
               ├── command-line-options.md (400 tokens)
               ├── comment-removal.md (200 tokens)
               ├── configuration.md (1600 tokens)
               ├── custom-instructions.md (200 tokens)
               ├── development/
                  ├── index.md (400 tokens)
                  ├── using-repomix-as-a-library.md (400 tokens)
               ├── github-actions.md (600 tokens)
               ├── index.md (200 tokens)
               ├── installation.md (300 tokens)
               ├── mcp-server.md (1200 tokens)
               ├── output.md (300 tokens)
               ├── prompt-examples.md (200 tokens)
               ├── remote-repository-processing.md (200 tokens)
               ├── security.md (200 tokens)
               ├── tips/
                  ├── best-practices.md (200 tokens)
               ├── usage.md (200 tokens)
            ├── index.md (700 tokens)
         ├── zh-tw/
            ├── guide/
               ├── code-compress.md (300 tokens)
               ├── command-line-options.md (400 tokens)
               ├── comment-removal.md (200 tokens)
               ├── configuration.md (1600 tokens)
               ├── custom-instructions.md (200 tokens)
               ├── development/
                  ├── index.md (400 tokens)
                  ├── using-repomix-as-a-library.md (400 tokens)
               ├── github-actions.md (600 tokens)
               ├── index.md (300 tokens)
               ├── installation.md (300 tokens)
               ├── mcp-server.md (1200 tokens)
               ├── output.md (200 tokens)
               ├── prompt-examples.md (200 tokens)
               ├── remote-repository-processing.md (200 tokens)
               ├── security.md (200 tokens)
               ├── tips/
                  ├── best-practices.md (200 tokens)
               ├── usage.md (200 tokens)
            ├── index.md (700 tokens)
      ├── tsconfig.json (100 tokens)
      ├── tsconfig.node.json (100 tokens)
   ├── compose.yml (200 tokens)
   ├── server/
      ├── .dockerignore (100 tokens)
      ├── .gcloudignore
      ├── .gitignore
      ├── Dockerfile (200 tokens)
      ├── cloudbuild.yaml (400 tokens)
      ├── package-lock.json (35.2k tokens)
      ├── package.json (100 tokens)
      ├── src/
         ├── constants.ts (100 tokens)
         ├── index.ts (900 tokens)
         ├── processZipFile.ts (1700 tokens)
         ├── remoteRepo.ts (800 tokens)
         ├── schemas/
            ├── request.ts (400 tokens)
         ├── types.ts (100 tokens)
         ├── utils/
            ├── cache.ts (400 tokens)
            ├── errorHandler.ts (300 tokens)
            ├── fileUtils.ts (700 tokens)
            ├── logger.ts (900 tokens)
            ├── network.ts (100 tokens)
            ├── processConcurrency.ts (100 tokens)
            ├── rateLimit.ts (200 tokens)
            ├── sharedInstance.ts (100 tokens)
            ├── time.ts (200 tokens)
            ├── validation.ts (200 tokens)
      ├── tsconfig.json (100 tokens)
```


## /.clinerules

```clinerules path="/.clinerules" 
# Important
- Follow all AI instructions in the `.github/instructions/` directory
- The base instructions are located in `.github/instructions/base.instructions.md`

```

## /.codecov.yml

```yml path="/.codecov.yml" 
coverage:
  status:
    patch:
      default:
        target: 80%
        informational: true
    project:
      default:
        target: 75%
        threshold: 1%

```

## /.cursorrules

```cursorrules path="/.cursorrules" 
# Important
- Follow all AI instructions in the `.github/instructions/` directory
- The base instructions are located in `.github/instructions/base.instructions.md`

```

## /.devcontainer/devcontainer.json

```json path="/.devcontainer/devcontainer.json" 
{
  "name": "Repomix",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bullseye",
  "runArgs": ["--name", "repomix-devcontainer"],
  "postCreateCommand": "npm install"
}

```

## /.dockerignore

```dockerignore path="/.dockerignore" 
node_modules
npm-debug.log
.git
.gitignore
.env
.env.*
*.md
.vscode
coverage
.nyc_output
dist
build

```

## /.github/CODEOWNERS

```github/CODEOWNERS path="/.github/CODEOWNERS" 
# Default owner for everything in the repo
* @yamadashy

```

## /.github/FUNDING.yml

```yml path="/.github/FUNDING.yml" 
github: yamadashy

```

## /.github/ISSUE_TEMPLATE/01_feature_request.yml

```yml path="/.github/ISSUE_TEMPLATE/01_feature_request.yml" 
name: 🚀 Feature Request
description: Suggest an idea or improvement for Repomix
labels:
  - enhancement
body:
  - type: markdown
    attributes:
      value: |
        Thank you for helping improve Repomix!

        We appreciate your feedback and will review your request as soon as possible.

  - type: textarea
    id: description
    attributes:
      label: Description
      description: "A clear and concise description of the feature you’d like to see."
      placeholder: |
        e.g. Add support for a `.repomixignore` file to exclude certain paths when packing.
    validations:
      required: true

```

## /.github/ISSUE_TEMPLATE/02_bug_report.yml

```yml path="/.github/ISSUE_TEMPLATE/02_bug_report.yml" 
name: 🐛 Bug Report
description: Report an unexpected behavior or error in Repomix
labels: [triage]
body:
  - type: markdown
    attributes:
      value: |
        Thank you for reporting an issue! We appreciate your help in making Repomix better.

        If you need real-time support, feel free to join our [Discord server](https://discord.gg/wNYzTwZFku).

        Please provide as much detail as possible.

  - type: textarea
    id: description
    attributes:
      label: Description
      description: "Please provide a concise description of what happened."
      placeholder: |
        e.g. Running `repomix --version` prints an error instead of the version.
    validations:
      required: true

  - type: dropdown
    id: usage_context
    attributes:
      label: Usage Context
      description: "Where did you encounter this issue?"
      options:
        - Repomix CLI
        - repomix.com

  - type: input
    id: repomix_version
    attributes:
      label: Repomix Version
      description: "Output of `repomix --version`"
      placeholder: "e.g. v0.3.5"

  - type: input
    id: node_version
    attributes:
      label: Node.js Version
      description: "Output of `node --version`"
      placeholder: "e.g. v18.16.0"

```

## /.github/ISSUE_TEMPLATE/config.yml

```yml path="/.github/ISSUE_TEMPLATE/config.yml" 
blank_issues_enabled: true

contact_links:
  - name: "💬 Discord Community"
    about: "Join our Discord server for support and discussion"
    url: "https://discord.gg/wNYzTwZFku"

```

## /.github/actions/repomix/action.yml

```yml path="/.github/actions/repomix/action.yml" 
name: "Repomix Action"
description: "Pack repository contents into a single file that is easy for LLMs to process"
author: "Kazuki Yamada <koukun0120@gmail.com>"
branding:
  icon: archive
  color: purple

inputs:
  directories:
    description: "Space-separated list of directories to process (defaults to '.')"
    required: false
    default: "."
  include:
    description: "Comma-separated glob patterns to include"
    required: false
    default: ""
  ignore:
    description: "Comma-separated glob patterns to ignore"
    required: false
    default: ""
  output:
    description: "Relative path to write packed file"
    required: false
    default: "repomix-output.xml"
  compress:
    description: "Set to 'false' to disable smart compression"
    required: false
    default: "true"
  style:
    description: "Output style (xml, markdown, plain)"
    required: false
    default: "xml"
  additional-args:
    description: "Any extra raw arguments to pass directly to the repomix CLI"
    required: false
    default: ""
  repomix-version:
    description: "Version (or tag) of the npm package to install – defaults to latest"
    required: false
    default: "latest"

runs:
  using: "composite"
  steps:
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: "24"
        cache: "npm"
    - name: Install Repomix
      shell: bash
      run: |
        npm install --global repomix@${{ inputs.repomix-version }}
    - name: Run Repomix
      id: build
      shell: bash
      run: |
        set -e
        # Using an array for safer command execution
        # Safely split directories input into an array, handling spaces correctly
        IFS=' ' read -r -a ARGS <<< "${{ inputs.directories }}"
        
        if [ -n "${{ inputs.include }}" ]; then
          ARGS+=(--include "${{ inputs.include }}")
        fi
        
        if [ -n "${{ inputs.ignore }}" ]; then
          ARGS+=(--ignore "${{ inputs.ignore }}")
        fi
        
        if [ "${{ inputs.compress }}" = "false" ]; then
          ARGS+=(--no-compress)
        else
          ARGS+=(--compress)
        fi
        
        if [ -n "${{ inputs.style }}" ]; then
          ARGS+=(--style "${{ inputs.style }}")
        fi
        
        ARGS+=(--output "${{ inputs.output }}")
        
        # Only add additional args if not empty
        if [ -n "${{ inputs.additional-args }}" ]; then
          # Use safer parsing for additional arguments
          IFS=' ' read -r -a ADDITIONAL_ARGS <<< "${{ inputs.additional-args }}"
          ARGS+=("${ADDITIONAL_ARGS[@]}")
        fi
        
        echo "Running: repomix ${ARGS[*]}"
        repomix "${ARGS[@]}"
        
        echo "output_file=${{ inputs.output }}" >> "$GITHUB_OUTPUT"

outputs:
  output_file:
    description: "Path to the file generated by Repomix"
    value: ${{ steps.build.outputs.output_file }}

```

## /.github/dependabot.yml

```yml path="/.github/dependabot.yml" 
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "daily"

```

## /.github/instructions/base.instructions.md

---
applyTo: '**'
---

# Repomix Project Structure and Overview

This document provides a structural overview of the Repomix project, designed to aid AI code assistants (like Copilot) in understanding the codebase.

Please refer to `README.md` for a complete and up-to-date project overview, and `CONTRIBUTING.md` for implementation guidelines and contribution procedures.

## Project Overview

Repomix is a tool that packs the contents of a software repository into a single file, making it easier for AI systems to analyze and process the codebase. It supports various output formats (XML, Markdown, or plain text), ignores files based on configurable patterns, and performs security checks to exclude potentially sensitive information.

## Directory Structure

The project is organized into the following directories:

```
repomix/
├── src/ # Main source code
│   ├── cli/ # Command-line interface logic (argument parsing, command handling, output)
│   ├── config/ # Configuration loading, schema, and defaults
│   ├── core/ # Core logic of Repomix
│   │   ├── file/ # File handling (reading, processing, searching, tree structure generation, git commands)
│   │   ├── metrics/ # Calculating code metrics (character count, token count)
│   │   ├── output/ # Output generation (different styles, headers, etc.)
│   │   ├── packager/ # Orchestrates file collection, processing, output, and clipboard operations.
│   │   ├── security/ # Security checks to exclude sensitive files
│   │   ├── mcp/ # MCP server integration (packaging codebases for AI analysis)
│   │   ├── tokenCount/ # Token counting using Tiktoken
│   │   └── treeSitter/ # Code parsing using Tree-sitter and language-specific queries
│   └── shared/ # Shared utilities and types (error handling, logging, helper functions)
├── tests/ # Unit and integration tests (organized mirroring src/)
│   ├── cli/
│   ├── config/
│   ├── core/
│   ├── integration-tests/
│   ├── shared/
│   └── testing/
└── website/ # Documentation website (VitePress)
    ├── client/      # Client-side code (Vue.js components, styles, configuration)
    │   ├── .vitepress/  # VitePress configuration and theme
    │   │   ├── config/  # Site configuration files (navigation, sidebar, etc.)
    │   │   └── theme/   # Custom theme and styles
    │   ├── components/ # Vue.js components for the website
    │   └── src/        # Markdown files for multilingual documentation
    └── server/      # Server-side API (for remote repository processing)
        └── src/       # Server source code (API endpoints, request handling)
```



# Coding Guidelines
- Follow the Airbnb JavaScript Style Guide.
- Split files into smaller, focused units when appropriate:
  - Aim to keep code files under 250 lines. If a file exceeds 250 lines, split it into multiple files based on functionality.
- Add comments to clarify non-obvious logic. **Ensure all comments are written in English.**
- Provide corresponding unit tests for all new features.
- After implementation, verify changes by running:
  ```bash
  npm run lint  # Ensure code style compliance
  npm run test  # Verify all tests pass
  ```

## Commit Messages
- Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification for all commit messages
- Always include a scope in your commit messages
- Format: `type(scope): Description`
  ```
  # Examples:
  feat(cli): Add new --no-progress flag
  fix(security): Handle special characters in file paths
  docs(website): Update installation guide
  style(website): Update GitHub sponsor button color
  refactor(core): Split packager into smaller modules
  test(cli): Add tests for new CLI options
  ```
- Types: feat, fix, docs, style, refactor, test, chore, etc.
- Scope should indicate the affected part of the codebase (cli, core, website, security, etc.)
- Description should be clear and concise in present tense
- Description must start with a capital letter

## Pull Request Guidelines
- All pull requests must follow the template:
  ```md
  <!-- Please include a summary of the changes -->

  ## Checklist

  - [ ] Run `npm run test`
  - [ ] Run `npm run lint`
  ```
- Include a clear summary of the changes at the top of the pull request description
- Reference any related issues using the format `#issue-number` 


## Dependencies and Testing
- Inject dependencies through a deps object parameter for testability
- Example:
  ```typescript
  export const functionName = async (
    param1: Type1,
    param2: Type2,
    deps = {
      defaultFunction1,
      defaultFunction2,
    }
  ) => {
    // Use deps.defaultFunction1() instead of direct call
  };
  ```
- Mock dependencies by passing test doubles through deps object
- Use vi.mock() only when dependency injection is not feasible

## Generate Comprehensive Output
- Include all content without abbreviation, unless specified otherwise
- Optimize for handling large codebases while maintaining output quality


## /.github/instructions/github-release-note.instructions.md

---
applyTo: '**'
---

# GitHub Release Note Guidelines
When writing release notes, please follow these guidelines:

- When referencing issues or PRs, use the gh command to verify the content:
  ```bash
  gh issue view <issue-number>  # For checking issue content
  gh pr view <pr-number>        # For checking PR content
  ```
  This helps ensure accuracy in release note descriptions.

Here are some examples of release notes that follow the guidelines:

v0.2.25
````md
This release brings significant improvements to output formatting and introduces flexible remote repository handling capabilities along with enhanced logging features.

# Improvements ⚡

## Remote Repository Enhancement (#335)
- Added branch/tag parsing directly from repository URLs:
```bash
repomix --remote https://github.com/yamadashy/repomix/tree/0.1.x
```
Functions identically to:
```bash
repomix --remote https://github.com/yamadashy/repomix --remote-branch 0.1.x
```

Special thanks to @huy-trn for implementing this user-friendly feature!

## Enhanced Output Formatting (#328, #329, #330)
- Added "End of Codebase" marker for better clarity in output
- Improved output header accuracy:
  - Better representation of codebase scope
  - Clear indication when using `--include` or `--ignore` options

Special thanks to @gitkenan for adding the "End of Codebase" marker and reporting the header issue!

## Path Pattern Support (#337)
- Added support for special characters in paths:
  - Handles parentheses in include patterns (e.g., `src/(categories)/**/*`)
  - Improved escaping for `[]` and `{}`
  - Essential for Next.js route groups and similar frameworks

Thank you @matheuscoelhomalta for improving path pattern support!

# How to Update

```bash
npm update -g repomix
```

---

As always, if you encounter any issues or have suggestions, please let us know through our GitHub issues or join our [Discord community](https://discord.gg/wNYzTwZFku) for support.
````

v0.2.24
````md
This release significantly enhances configuration flexibility with comprehensive CLI flag support and expands default ignore patterns for better project scaffolding. 

# What's New 🚀

## CLI Flags Revolution (#324)
- New command-line configuration now available.

```
- `--no-gitignore`: Disable .gitignore file usage
- `--no-default-patterns`: Disable default patterns
- `--header-text <text>`: Custom text to include in the file header
- `--instruction-file-path <path>`: Path to a file containing detailed custom instructions
- `--include-empty-directories`: Include empty directories in the output
```

Special recognition to @massdo for driving ecosystem growth.

# Improvements ⚡

## Enhanced Ignore Patterns (#318, #322)
- Expanded default ignores for Rust projects:
  - `target/`, `Cargo.lock`, build artifacts
  - PHP, Ruby, Go, Elixir, Haskell: package manager lock files

To @boralg for helping curate Rust-specific patterns!

# How to Update
```bash
npm update -g repomix
```

---

As always, if you encounter any issues or have suggestions, please let us know through our GitHub issues or join our [Discord community](https://discord.gg/wNYzTwZFku) for support.
````

v0.2.23
````md
This release adds significant performance improvements for large repositories, making Repomix faster and more efficient when needed.

# Improvements ⚡

## Parallel Processing Enhancement (#309)
- Implemented worker threads using [Piscina](https://github.com/piscinajs/piscina) for parallel processing

### Benchmark Results
- `yamadashy.repomix`: No significant change
  - Before: 868.73 millis
  - After: 671.26 millis
- `facebook/react`: 29x faster
  - Before: 123.31 secs
  - After: 4.19 secs
- `vercel/next.js`: 58x faster
  - Before: 17.85 mins
  - After: 17.27 secs

Note: While Repomix is not primarily designed for processing large repositories, and speed is not a primary goal, faster processing can provide a better user experience when working with larger codebases.

# How to Update

```bash
npm update -g repomix
```


---

As always, if you encounter any issues or have suggestions, please let us know through our GitHub issues or join our [Discord community](https://discord.gg/wNYzTwZFku) for support.
````

v0.2.22
````md
This release introduces significant improvements to large file handling and expands the Repomix ecosystem with new tools and community channels.

# Improvements ⚡ 

## Improved Large File Handling (#302)

- Added a file size limit check (50MB) to prevent memory issues
- Graceful error handling for large files with clear user guidance:

Special thanks to @slavashvets for their continued contributions!

# Ecosystem Growth 🤝 

## New VS Code Extension (#300)
A community-created VS Code extension "Repomix Runner" is now available:
- Run Repomix directly from VS Code
- Extension by @massdo: [View on VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=DorianMassoulier.repomix-runner)

Thank you @massdo for bringing Repomix to VS Code and expanding our tooling ecosystem!

## Official Social Media
- Launched official Repomix X (Twitter) account: [@repomix_ai](https://x.com/repomix_ai)
  - Follow for updates, tips, and community highlights

# How to Update

```bash
npm update -g repomix
```

---

Join our growing community on [Discord](https://discord.gg/BF8GxZHE2C) and follow us on [X](https://x.com/repomix_ai) for updates!
````

v0.2.21
````md
This release introduces significant improvements to output formatting and documentation, featuring a new parsable style option for enhanced XML handling.

# What's New 🚀 

## Enhanced Output Style Control (#287)
- Added new `parsableStyle` option for better output handling:
  - Ensures output strictly follows the specification of the chosen format
  - Provides properly escaped XML output with fast-xml-parser
  - Dynamically adjusts markdown code block delimiters to avoid content conflicts
- Available via CLI flag `--parsable-style` or in configuration file

Special thanks to @atollk for their first contribution!

# Documentation 📚

## README Enhancements (#296)
- Updated Homebrew installation documentation to include Linux support

Special thanks to @chenrui333 for their continued contributions!

## Website Multi-Language Support (#293)
- Enhanced multi-language support in [repomix.com](https://repomix.com)

# How to Update

To update to the latest version, run:
```bash
npm update -g repomix
```


---

As always, if you encounter any issues or have suggestions, please let us know through our GitHub issues or join our [Discord community](https://discord.gg/wNYzTwZFku) for support.
````


## /.github/instructions/website.instructions.md

---
applyTo: '**'
---

# Website Documentation

## Supported Languages
The website documentation is available in the following languages:
- English (en)
- Japanese (日本語) (ja)
- Chinese Simplified (简体中文) (zh-cn)
- Chinese Traditional (繁體中文) (zh-tw)
- Korean (한국어) (ko)
- German (Deutsch) (de)
- French (Français) (fr)
- Spanish (Español) (es)
- Portuguese Brazilian (Português do Brasil) (pt-br)
- Indonesian (Bahasa Indonesia) (id)
- Vietnamese (Tiếng Việt) (vi)

All translations should be accurate and maintain consistent terminology across languages. When adding new features or documentation, please ensure that the English version is updated first, followed by translations in other languages.

## Navigation Configuration
When modifying website navigation or adding new pages:
1. Update the configuration files in `website/client/.vitepress/config/`.

Ensure all language configurations are synchronized to maintain consistency across the documentation.

## Adding New Languages
When adding support for a new language, follow these steps:

1. Create a configuration file (e.g., `configXx.ts`) in `website/client/.vitepress/config/` based on existing language configurations
2. Include proper sidebar navigation, labels, and search translations
3. Update the imports and locale entries in the main VitePress configuration (`config.ts`)
4. Add search configurations to `configShard.ts`
5. Update the supported languages list in this file
6. Create directory structure for content (e.g., `website/client/src/xx/`)
7. Create content files starting with main index page and guide index
8. Progressively translate remaining documentation pages
9. Test navigation and search functionality in the new language

When working on multiple languages simultaneously, approach one language at a time completely before moving to the next language to maintain quality and consistency.


## /.github/pull_request_template.md

<!-- Please include a summary of the changes -->

## Checklist

- [ ] Run `npm run test`
- [ ] Run `npm run lint`


## /.github/renovate.json5

```json5 path="/.github/renovate.json5" 
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:recommended",
    "schedule:weekly",
    'group:allNonMajor'
  ],
  "rangeStrategy": "bump",
  "dependencyDashboard": false,
  "labels": ["dependencies", "renovate"],
  "packageRules": [
    {
      matchDepTypes: ['peerDependencies'],
      enabled: false,
    },
  ],
  "ignoreDeps": [
    "node",
  ]
}

```

## /.github/workflows/ci.yml

```yml path="/.github/workflows/ci.yml" 
name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  workflow_dispatch:

jobs:
  lint-biome:
    name: Lint Biome
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version-file: .tool-versions
        cache: npm
    - run: npm ci
    - run: npm run lint-biome && git diff --exit-code

  lint-ts:
    name: Lint TypeScript
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version-file: .tool-versions
        cache: npm
    - run: npm ci
    - run: npm run lint-ts

  lint-secretlint:
    name: Lint Secretlint
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version-file: .tool-versions
        cache: npm
    - run: npm ci
    - run: npm run lint-secretlint

  lint-action:
    name: Lint GitHub Actions
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker://rhysd/actionlint:latest
        with:
          args: "-color"

  check-typos:
    name: Check typos
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: crate-ci/typos@master

  test:
    name: Test
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [18.0.0, 18.x, 19.x, 20.x, 21.x, 22.x, 23.x, 24.x]
    runs-on: ${{ matrix.os }}
    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm ci
    - run: npm run test --reporter=verbose
      env:
        CI_OS: ${{ runner.os }}

  test-coverage:
    name: Test coverage
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version-file: .tool-versions
        cache: npm
    - run: npm ci
    - run: npm run test-coverage -- --reporter=verbose
      env:
        CI_OS: ${{ runner.os }}
    - uses: actions/upload-artifact@v4
      with:
        name: test-coverage
        path: coverage/
    - uses: codecov/codecov-action@v5
      with:
        fail_ci_if_error: true
        directory: ./coverage
      env:
        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

  build-and-run:
    name: Build and run
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [18.0.0, 18.x, 19.x, 20.x, 21.x, 22.x, 23.x, 24.x]
    runs-on: ${{ matrix.os }}
    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm ci
    - run: npm run build
    - name: Install only production dependencies
      run: npm ci --omit=dev
    - run: node bin/repomix.cjs
    - run: node bin/repomix.cjs --version
    - run: node bin/repomix.cjs --help
    - name: Upload build artifact
      uses: actions/upload-artifact@v4
      with:
        name: repomix-output-${{ matrix.os }}-${{ matrix.node-version }}.txt
        path: repomix-output.txt

```

## /.github/workflows/claude.yml

```yml path="/.github/workflows/claude.yml" 
name: Claude Code

on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
  issues:
    types: [opened, assigned]
  pull_request_review:
    types: [submitted]

jobs:
  claude:
    if: |
      (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
      (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: read
      issues: read
      id-token: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - name: Run Claude Code
        id: claude
        uses: anthropics/claude-code-action@beta
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}


```

## /.github/workflows/codeql.yml

```yml path="/.github/workflows/codeql.yml" 
name: "CodeQL"

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
  schedule:
    - cron: '25 11 * * 0'

jobs:
  analyze:
    name: Analyze (${{ matrix.language }})
    runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
    permissions:
      security-events: write
      packages: read
      actions: read
      contents: read

    strategy:
      fail-fast: false
      matrix:
        include:
        - language: javascript-typescript
          build-mode: none
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3
      with:
        languages: ${{ matrix.language }}
        build-mode: ${{ matrix.build-mode }}

    - if: matrix.build-mode == 'manual'
      shell: bash
      run: |
        echo 'If you are using a "manual" build mode for one or more of the' \
          'languages you are analyzing, replace this with the commands to build' \
          'your code, for example:'
        echo '  make bootstrap'
        echo '  make release'
        exit 1

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3
      with:
        category: "/language:${{matrix.language}}"

```

## /.github/workflows/docker.yml

```yml path="/.github/workflows/docker.yml" 
name: Docker

on:
  push:
    branches:
      - "main"
    paths-ignore:
      - "**.md"
      - LICENSE
  pull_request:
    branches:
      - "*"
    paths:
      - "Dockerfile"
      - ".dockerignore"
      - ".github/workflows/docker.yml"
  workflow_dispatch:
  release:
    types: [published, edited]

permissions:
  contents: read
  packages: write

jobs:
  build-and-publish-image:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: |
            ghcr.io/yamadashy/repomix
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}
            type=semver,pattern={{major}}.{{minor}}
            type=raw,value=latest,enable=${{ github.event_name == 'release' }}

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to GitHub Container Registry
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and Publish Docker Image
        uses: docker/build-push-action@v6
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          platforms: linux/amd64,linux/arm64,linux/arm/v7
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

```

## /.github/workflows/homebrew.yml

```yml path="/.github/workflows/homebrew.yml" 
name: Homebrew

on:
  release:
    types:
      - created

jobs:
  homebrew:
    runs-on: macos-latest
    steps:
      - name: Set up Homebrew
        uses: Homebrew/actions/setup-homebrew@master
        with:
          test-bot: false

      - name: Configure Git user
        uses: Homebrew/actions/git-user-config@master

      - name: Bump packages
        uses: Homebrew/actions/bump-packages@master
        with:
          token: ${{ secrets.COMMITTER_TOKEN }}
          formulae: repomix

```

## /.github/workflows/pack-repository.yml

```yml path="/.github/workflows/pack-repository.yml" 
name: Pack repository with Repomix

on:
  workflow_dispatch:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  pack-repo:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Pack repository with Repomix
        uses: yamadashy/repomix/.github/actions/repomix@main
        with:
          output: repomix-output.xml

      - name: Upload Repomix output
        uses: actions/upload-artifact@v4
        with:
          name: repomix-output.xml
          path: repomix-output.xml
          retention-days: 30

```

## /.github/workflows/test-action.yml

```yml path="/.github/workflows/test-action.yml" 
name: Test Repomix Action

on:
  workflow_dispatch:
  push:
    paths:
      - '.github/actions/repomix/**'

jobs:
  test-action:
    name: Test Node.js ${{ matrix.node-version }}
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]
        include:
          - node-version: 18
            test-case: "minimal"
          - node-version: 20
            test-case: "basic"
          - node-version: 22
            test-case: "full"
    steps:
      - uses: actions/checkout@v4

      - name: Run Repomix Action (Minimal)
        if: matrix['test-case'] == 'minimal'
        uses: ./.github/actions/repomix
        with:
          output: "repomix-minimal-output.txt"

      - name: Run Repomix Action (Basic)
        if: matrix['test-case'] == 'basic'
        uses: ./.github/actions/repomix
        with:
          directories: "src"
          include: "**/*.ts"
          output: "repomix-basic-output.txt"
          compress: "true"

      - name: Run Repomix Action (Full)
        if: matrix['test-case'] == 'full'
        uses: ./.github/actions/repomix
        with:
          directories: "src tests"
          include: "**/*.ts,**/*.md"
          ignore: "**/*.test.ts"
          output: "repomix-full-output.txt"
          compress: "true"
          additional-args: "--no-file-summary"

      - name: Upload result
        uses: actions/upload-artifact@v4
        with:
          name: repomix-output-node${{ matrix.node-version }}
          path: repomix-*-output.txt

```

## /.gitignore

```gitignore path="/.gitignore" 
# Dependency directories
node_modules/

# Build output
lib/

# Logs
*.log

# OS generated files
.DS_Store

# Editor directories and files
.vscode/
.idea/

# Test coverage
coverage/

# Temporary files
*.tmp
*.temp

# Repomix output
repomix-output.txt
repomix-output.xml
repomix-output.md

# ESLint cache
.eslintcache

# yarn
.yarn/

# biome
.biome/

# aider
.aider*

# private files
.cursor/rules/
.github/todo.md

```

## /.node-version

```node-version path="/.node-version" 
23.6.0

```

## /.npmignore

```npmignore path="/.npmignore" 
# Source files
src/

# Test files
tests/
coverage/

# Configuration files
tsconfig.json
tsconfig.build.json
.eslintrc.js
eslint.config.mjs
prettier.config.mjs
vite.config.ts
biome.json

# Git files
.gitignore
.git

# CI files
.github/

# yarn files
.yarn

# ESLint files
.eslintcache

# Config files
.editorconfig
.node-version
.tool-versions
repomix.config.js

# Editor files
.vscode/
.idea/
.memo/

# Logs
*.log

# Repomix output
repomix-output.txt

# Development scripts
scripts/

# Documentation files (except README and LICENSE)
docs/
CONTRIBUTING.md
CHANGELOG.md

# Temporary files
*.tmp
*.temp

# OS generated files
.DS_Store
Thumbs.db

# biome
.biome/

# Website
website/

# Devcontainer
.devcontainer/

# Browser
browser/

```

## /.repomixignore

```repomixignore path="/.repomixignore" 
node_modules
.yarn
.eslinttcache
tests/integration-tests/fixtures

```

## /.secretlintrc.json

```json path="/.secretlintrc.json" 
{
  "rules": [
    {
      "id": "@secretlint/secretlint-rule-preset-recommend"
    }
  ]
}

```

## /.tool-versions

```tool-versions path="/.tool-versions" 
nodejs 24.0.1

```

## /CLAUDE.md

# Repomix Development Guide

## Build & Development Commands
- `npm run build` - Build the project
- `npm run lint` - Run all linters (Biome, TypeScript, secretlint)
- `npm run lint-biome` - Run Biome linter with auto-fix
- `npm run lint-ts` - Run TypeScript type checking
- `npm test` - Run all tests
- `npm test -- /path/to/file.test.ts` - Run a specific test file
- `npm test -- -t "test name"` - Run tests matching description
- `npm run test-coverage` - Run tests with coverage report
- `npm run repomix` - Build and run the CLI tool

## Code Style Guidelines
- **Formatting**: 2 spaces indentation, 120 char line width, single quotes, trailing commas
- **Imports**: Use `node:` prefix for built-ins, `.js` extension for relative imports, `import type` for types
- **TypeScript**: Strict type checking, explicit return types, prefer interfaces for object types
- **Error Handling**: Custom errors extend `RepomixError`, descriptive messages, proper try/catch
- **Dependencies**: Inject dependencies through deps object parameter for testability
- **File Structure**: Keep files under 250 lines, organize by feature, use workers for concurrent operations
- **Testing**: Use Vitest, mock dependencies, descriptive test names, arrange/act/assert pattern

## Naming Conventions
- PascalCase: Classes, interfaces, types
- camelCase: Variables, functions, methods, file names
- Test files: `[filename].test.ts`

## Git Workflow
- Write detailed commit messages focusing on the "why" rather than the "what"
- Run `npm run lint && npm test` before committing changes


## /CODE_OF_CONDUCT.md

# Repomix Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we, as contributors and maintainers, pledge to make participation in our project and community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contribute to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as physical or electronic addresses, without explicit permission
* Other conduct that could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that do not align with this Code of Conduct or to ban temporarily or permanently any contributor for behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [koukun0120@gmail.com](mailto:koukun0120@gmail.com). All complaints will be reviewed and investigated, resulting in a response deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality concerning the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html).

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this Code of Conduct, see [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq).


## /CONTRIBUTING.md

# Contribution Guide

Thanks for your interest in **Repomix**! 🚀 We'd love your help to make it even better. Here's how you can get involved:


- **Create an Issue**: Spot a bug? Have an idea for a new feature? Let us know by creating an issue.
- **Submit a Pull Request**: Found something to fix or improve? Jump in and submit a PR!
- **Spread the Word**: Share your experience with Repomix on social media, blogs, or with your tech community.
- **Use Repomix**: The best feedback comes from real-world usage, so feel free to integrate Repomix into your own projects!

## Maintainers

Repomix is maintained by Yamadashy ([@yamadashy](https://github.com/yamadashy)). While all contributions are welcome, please understand that not every suggestion may be accepted if they don't align with the project's goals or coding standards.

---

## Pull Requests

Before submitting a Pull Request, please ensure:

1. Your code passes all tests: Run `npm run test`
2. Your code adheres to our linting standards: Run `npm run lint`
3. You have updated relevant documentation (especially README.md) if you've added or changed functionality.

## Local Development

To set up Repomix for local development:

```bash
git clone https://github.com/yamadashy/repomix.git
cd repomix
npm install
```

To run Repomix locally:

```bash
npm run repomix
```

### Docker Usage
You can also run Repomix using Docker. Here's how:

First, build the Docker image:
```bash
docker build -t repomix .
```

Then, run the Docker container:
```bash
docker run -v ./:/app -it --rm repomix
```

### Coding Style

We use [Biome](https://biomejs.dev/) for linting and formatting. Please make sure your code follows the style guide by running:

```bash
npm run lint
```

### Testing

We use [Vitest](https://vitest.dev/) for testing. To run the tests:

```bash
npm run test
```

For test coverage:

```bash
npm run test-coverage
```

### Documentation

When adding new features or making changes, please update the relevant documentation in the README.md file.

### Website Development

The Repomix website is built with [VitePress](https://vitepress.dev/). To run the website locally:

```bash
# Prerequisites: Docker must be installed on your system

# Start the website development server
npm run website

# Access the website at http://localhost:5173/
```

The website source code is located in the `website` directory. The main components are:

- `website/client`: Frontend code (Vue.js components, styles, etc.)
- `website/server`: Backend API server

When updating documentation, you only need to update the English version (`website/client/src/en/`).
The maintainers will handle translations to other languages.

## Releasing

New versions are managed by the maintainer. If you think a release is needed, open an issue to discuss it

Thank you for contributing to Repomix!


## /Dockerfile

``` path="/Dockerfile" 
FROM node:22-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
    git \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

RUN mkdir /repomix
WORKDIR /repomix

# Install dependencies and build repomix, then link the package to the global scope
# To reduce the size of the layer, npm ci and npm link are executed in the same RUN command
COPY . .
RUN npm ci \
    && npm run build \
    && npm link \
    && npm ci --omit=dev \
    && npm cache clean --force

WORKDIR /app

# Check the operation of repomix
RUN repomix --version
RUN repomix --help

ENTRYPOINT ["repomix"]

```

## /README.md

<div align="center" markdown="1">
   <sup>Special thanks to:</sup>
   <br>
   <br>
   <a href="https://www.warp.dev/repomix">
      <img alt="Warp sponsorship" width="400" src="website/client/src/public/images/sponsors/warp/Terminal-Image.png">
   </a>

### [Warp, the agent terminal for developers](https://www.warp.dev/repomix)
[Available for MacOS, Linux, & Windows](https://www.warp.dev/repomix)<br>

</div>

<hr />

<div align="center">
  <a href="https://repomix.com">
    <img src="website/client/src/public/images/repomix-title.png" alt="Repomix" width="500" height="auto" />
  </a>
  <p align="center">
    <b>Pack your codebase into AI-friendly formats</b>
  </p>
</div>

<p align="center">
  <a href="https://repomix.com"><b>Use Repomix online! 👉 repomix.com</b></a><br>
</p>

<p align="center">
  Need discussion? Join us on <a href="https://discord.gg/wNYzTwZFku">Discord</a>!<br>
  <i>Share your experience and tips</i><br>
  <i>Stay updated on new features</i><br>
  <i>Get help with configuration and usage</i><br>
</p>

<hr />

[![npm](https://img.shields.io/npm/v/repomix.svg?maxAge=1000)](https://www.npmjs.com/package/repomix)
[![npm](https://img.shields.io/npm/d18m/repomix)](https://www.npmjs.com/package/repomix)
[![Actions Status](https://github.com/yamadashy/repomix/actions/workflows/ci.yml/badge.svg)](https://github.com/yamadashy/repomix/actions?query=workflow%3A"ci")
[![codecov](https://codecov.io/github/yamadashy/repomix/graph/badge.svg)](https://codecov.io/github/yamadashy/repomix)
[![Sponsors](https://img.shields.io/github/sponsors/yamadashy?logo=github)](https://github.com/sponsors/yamadashy)
[![Discord](https://badgen.net/discord/online-members/wNYzTwZFku?icon=discord&label=discord)](https://discord.gg/wNYzTwZFku)

[![DeepWiki](https://img.shields.io/badge/DeepWiki-yamadashy%2Frepomix-blue.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXMGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==)](https://deepwiki.com/yamadashy/repomix)
<!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->

📦 Repomix is a powerful tool that packs your entire repository into a single, AI-friendly file.  
It is perfect for when you need to feed your codebase to Large Language Models (LLMs) or other AI tools like Claude,
ChatGPT, DeepSeek, Perplexity, Gemini, Gemma, Llama, Grok, and more.

## 🏆 Open Source Awards Nomination

We're honored! Repomix has been nominated for the **Powered by AI** category at the [JSNation Open Source Awards 2025](https://osawards.com/javascript/).

This wouldn't have been possible without all of you using and supporting Repomix. Thank you!

If Repomix has helped you analyze or pack codebases for AI tools, we'd be grateful for your vote in the **Powered by AI** category.

You can vote here: [https://forms.gle/5QaYBM6pNoyWLfL2A](https://forms.gle/5QaYBM6pNoyWLfL2A)

Thank you for your support!

## 🎉 New: Repomix Website & Discord Community!

- Try Repomix in your browser at [repomix.com](https://repomix.com/)
- Join our [Discord Server](https://discord.gg/wNYzTwZFku) for support and discussion

**We look forward to seeing you there!**

## 🌟 Features

- **AI-Optimized**: Formats your codebase in a way that's easy for AI to understand and process.
- **Token Counting**: Provides token counts for each file and the entire repository, useful for LLM context limits.
- **Simple to Use**: You need just one command to pack your entire repository.
- **Customizable**: Easily configure what to include or exclude.
- **Git-Aware**: Automatically respects your `.gitignore` files and `.git/info/exclude`.
- **Security-Focused**: Incorporates [Secretlint](https://github.com/secretlint/secretlint) for robust security checks to detect and prevent inclusion of sensitive information.
- **Code Compression**: The `--compress` option uses [Tree-sitter](https://github.com/tree-sitter/tree-sitter) to extract key code elements, reducing token count while preserving structure.

## 🚀 Quick Start

### Using the CLI Tool `>_`

You can try Repomix instantly in your project directory without installation:

```bash
npx repomix
```

Or install globally for repeated use:

```bash
# Install using npm
npm install -g repomix

# Alternatively using yarn
yarn global add repomix

# Alternatively using Homebrew (macOS/Linux)
brew install repomix

# Then run in any project directory
repomix
```

That's it! Repomix will generate a `repomix-output.xml` file in your current directory, containing your entire
repository in an AI-friendly format.

You can then send this file to an AI assistant with a prompt like:

```
This file contains all the files in the repository combined into one.
I want to refactor the code, so please review it first.
```

![Repomix File Usage 1](website/client/src/public/images/docs/repomix-file-usage-1.png)

When you propose specific changes, the AI might be able to generate code accordingly. With features like Claude's
Artifacts, you could potentially output multiple files, allowing for the generation of multiple interdependent pieces of
code.

![Repomix File Usage 2](website/client/src/public/images/docs/repomix-file-usage-2.png)

Happy coding! 🚀

### Using The Website 🌐

Want to try it quickly? Visit the official website at [repomix.com](https://repomix.com). Simply enter your repository
name, fill in any optional details, and click the **Pack** button to see your generated output.

#### Available Options

The website offers several convenient features:

- Customizable output format (XML, Markdown, or Plain Text)
- Instant token count estimation
- Much more!

### Using The Browser Extension 🧩

Get instant access to Repomix directly from any GitHub repository! Our Chrome extension adds a convenient "Repomix" button to GitHub repository pages.

![Repomix Browser Extension](website/client/src/public/images/docs/browser-extension.png)

#### Install
- Chrome Extension: [Repomix - Chrome Web Store](https://chromewebstore.google.com/detail/repomix/fimfamikepjgchehkohedilpdigcpkoa)
- Firefox Add-on: Coming soon

#### Features
- One-click access to Repomix for any GitHub repository
- More exciting features coming soon!

### Using The VSCode Extension ⚡️

A community-maintained VSCode extension called [Repomix Runner](https://marketplace.visualstudio.com/items?itemName=DorianMassoulier.repomix-runner) (created by [massdo](https://github.com/massdo)) lets you run Repomix right inside your editor with just a few clicks. Run it on any folder, manage outputs seamlessly, and control everything through VSCode's intuitive interface. 

Want your output as a file or just the content? Need automatic cleanup? This extension has you covered. Plus, it works smoothly with your existing repomix.config.json.

Try it now on the [VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=DorianMassoulier.repomix-runner)!
Source code is available on [GitHub](https://github.com/massdo/repomix-runner).

### Alternative Tools 🛠️

If you're using Python, you might want to check out `Gitingest`, which is better suited for Python ecosystem and data
science workflows:
https://github.com/cyclotruc/gitingest

## 📊 Usage

To pack your entire repository:

```bash
repomix
```

To pack a specific directory:

```bash
repomix path/to/directory
```

To pack specific files or directories
using [glob patterns](https://github.com/mrmlnc/fast-glob?tab=readme-ov-file#pattern-syntax):

```bash
repomix --include "src/**/*.ts,**/*.md"
```

To exclude specific files or directories:

```bash
repomix --ignore "**/*.log,tmp/"
```

To pack a remote repository:

```bash
repomix --remote https://github.com/yamadashy/repomix

# You can also use GitHub shorthand:
repomix --remote yamadashy/repomix

# You can specify the branch name, tag, or commit hash:
repomix --remote https://github.com/yamadashy/repomix --remote-branch main

# Or use a specific commit hash:
repomix --remote https://github.com/yamadashy/repomix --remote-branch 935b695

# Another convenient way is specifying the branch's URL
repomix --remote https://github.com/yamadashy/repomix/tree/main

# Commit's URL is also supported
repomix --remote https://github.com/yamadashy/repomix/commit/836abcd7335137228ad77feb28655d85712680f1

```

To compress the output:

```bash
repomix --compress

# You can also use it with remote repositories:
repomix --remote yamadashy/repomix --compress
```

To initialize a new configuration file (`repomix.config.json`):

```bash
repomix --init
```

Once you have generated the packed file, you can use it with Generative AI tools like ChatGPT, DeepSeek, Perplexity, Gemini, Gemma, Llama, Grok, and more.

### Docker Usage 🐳

You can also run Repomix using Docker.  
This is useful if you want to run Repomix in an isolated environment or prefer using containers.

Basic usage (current directory):

```bash
docker run -v .:/app -it --rm ghcr.io/yamadashy/repomix
```

To pack a specific directory:

```bash
docker run -v .:/app -it --rm ghcr.io/yamadashy/repomix path/to/directory
```

Process a remote repository and output to a `output` directory:

```bash
docker run -v ./output:/app -it --rm ghcr.io/yamadashy/repomix --remote https://github.com/yamadashy/repomix
```

### Prompt Examples

Once you have generated the packed file with Repomix, you can use it with AI tools like ChatGPT, DeepSeek, Perplexity, Gemini, Gemma, Llama, Grok, and more.
Here are some example prompts to get you started:

#### Code Review and Refactoring

For a comprehensive code review and refactoring suggestions:

```
This file contains my entire codebase. Please review the overall structure and suggest any improvements or refactoring opportunities, focusing on maintainability and scalability.
```

#### Documentation Generation

To generate project documentation:

```
Based on the codebase in this file, please generate a detailed README.md that includes an overview of the project, its main features, setup instructions, and usage examples.
```

#### Test Case Generation

For generating test cases:

```
Analyze the code in this file and suggest a comprehensive set of unit tests for the main functions and classes. Include edge cases and potential error scenarios.
```

#### Code Quality Assessment

Evaluate code quality and adherence to best practices:

```
Review the codebase for adherence to coding best practices and industry standards. Identify areas where the code could be improved in terms of readability, maintainability, and efficiency. Suggest specific changes to align the code with best practices.
```

#### Library Overview

Get a high-level understanding of the library

```
This file contains the entire codebase of library. Please provide a comprehensive overview of the library, including its main purpose, key features, and overall architecture.
```

Feel free to modify these prompts based on your specific needs and the capabilities of the AI tool you're using.

### Community Discussion

Check out our [community discussion](https://github.com/yamadashy/repomix/discussions/154) where users share:

- Which AI tools they're using with Repomix
- Effective prompts they've discovered
- How Repomix has helped them
- Tips and tricks for getting the most out of AI code analysis

Feel free to join the discussion and share your own experiences! Your insights could help others make better use of
Repomix.

### Output File Format

Repomix generates a single file with clear separators between different parts of your codebase.  
To enhance AI comprehension, the output file begins with an AI-oriented explanation, making it easier for AI models to
understand the context and structure of the packed repository.

#### XML Format (default)

The XML format structures the content in a hierarchical manner:

```xml
This file is a merged representation of the entire codebase, combining all repository files into a single document.

<file_summary>
  (Metadata and usage AI instructions)
</file_summary>

<directory_structure>
src/
cli/
cliOutput.ts
index.ts

(...remaining directories)
</directory_structure>

<files>
<file path="src/index.js">
  // File contents here
</file>

(...remaining files)
</files>

<instruction>
(Custom instructions from `output.instructionFilePath`)
</instruction>
```

For those interested in the potential of XML tags in AI contexts:  
https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/use-xml-tags

> When your prompts involve multiple components like context, instructions, and examples, XML tags can be a
> game-changer. They help Claude parse your prompts more accurately, leading to higher-quality outputs.

This means that the XML output from Repomix is not just a different format, but potentially a more effective way to feed
your codebase into AI systems for analysis, code review, or other tasks.

#### Markdown Format

To generate output in Markdown format, use the `--style markdown` option:

```bash
repomix --style markdown
```

The Markdown format structures the content in a hierarchical manner:

````markdown
This file is a merged representation of the entire codebase, combining all repository files into a single document.

# File Summary

(Metadata and usage AI instructions)

# Repository Structure

```
src/
  cli/
    cliOutput.ts
    index.ts
```

(...remaining directories)

# Repository Files

## File: src/index.js

```
// File contents here
```

(...remaining files)

# Instruction

(Custom instructions from `output.instructionFilePath`)
````

This format provides a clean, readable structure that is both human-friendly and easily parseable by AI systems.

#### Plain Text Format

To generate output in plain text format, use the `--style plain` option:

```bash
repomix --style plain
```

```text
This file is a merged representation of the entire codebase, combining all repository files into a single document.

================================================================
File Summary
================================================================
(Metadata and usage AI instructions)

================================================================
Directory Structure
================================================================
src/
  cli/
    cliOutput.ts
    index.ts
  config/
    configLoader.ts

(...remaining directories)

================================================================
Files
================================================================

================
File: src/index.js
================
// File contents here

================
File: src/utils.js
================
// File contents here

(...remaining files)

================================================================
Instruction
================================================================
(Custom instructions from `output.instructionFilePath`)
```

### Command Line Options

#### Basic Options
- `-v, --version`: Show tool version

#### Output Options
- `-o, --output <file>`: Specify the output file name
- `--stdout`: Output to stdout instead of writing to a file (cannot be used with `--output` option)
- `--style <style>`: Specify the output style (`xml`, `markdown`, `plain`)
- `--parsable-style`: Enable parsable output based on the chosen style schema. Note that this can increase token count.
- `--compress`: Perform intelligent code extraction, focusing on essential function and class signatures to reduce token count
- `--output-show-line-numbers`: Show line numbers in the output
- `--copy`: Additionally copy generated output to system clipboard
- `--no-file-summary`: Disable file summary section output
- `--no-directory-structure`: Disable directory structure section output
- `--remove-comments`: Remove comments from supported file types
- `--remove-empty-lines`: Remove empty lines from the output
- `--header-text <text>`: Custom text to include in the file header
- `--instruction-file-path <path>`: Path to a file containing detailed custom instructions
- `--include-empty-directories`: Include empty directories in the output
- `--include-diffs`: Include git diffs in the output (includes both work tree and staged changes separately)
- `--no-git-sort-by-changes`: Disable sorting files by git change count (enabled by default)

#### Filter Options
- `--include <patterns>`: List of include patterns (comma-separated)
- `-i, --ignore <patterns>`: Additional ignore patterns (comma-separated)
- `--no-gitignore`: Disable .gitignore file usage
- `--no-default-patterns`: Disable default patterns

#### Remote Repository Options
- `--remote <url>`: Process a remote Git repository
- `--remote-branch <name>`: Specify the remote branch name, tag, or commit hash (defaults to repository default branch)

#### Configuration Options
- `-c, --config <path>`: Path to a custom config file
- `--init`: Create config file
- `--global`: Use global config

#### Security Options
- `--no-security-check`: Disable security check

#### Token Count Options
- `--token-count-encoding <encoding>`: Specify token count encoding used by OpenAI's [tiktoken](https://github.com/openai/tiktoken) tokenizer (e.g., `o200k_base` for GPT-4o, `cl100k_base` for GPT-4/3.5). See [tiktoken model.py](https://github.com/openai/tiktoken/blob/main/tiktoken/model.py#L24) for encoding details.

#### MCP
- `--mcp`: Run as a [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server

#### Other Options
- `--top-files-len <number>`: Number of top files to display in the summary
- `--verbose`: Enable verbose logging
- `--quiet`: Disable all output to stdout

Examples:

```bash
# Basic usage
repomix

# Custom output
repomix -o output.xml --style xml

# Output to stdout
repomix --stdout > custom-output.txt

# Send output to stdout, then pipe into another command (for example, simonw/llm)
repomix --stdout | llm "Please explain what this code does."

# Custom output with compression
repomix --compress

# Process specific files
repomix --include "src/**/*.ts" --ignore "**/*.test.ts"

# Remote repository with branch
repomix --remote https://github.com/user/repo/tree/main

# Remote repository with commit
repomix --remote https://github.com/user/repo/commit/836abcd7335137228ad77feb28655d85712680f1

# Remote repository with shorthand
repomix --remote user/repo
```

### Updating Repomix

To update a globally installed Repomix:

```bash
# Using npm
npm update -g repomix

# Using yarn
yarn global upgrade repomix
```

Using `npx repomix` is generally more convenient as it always uses the latest version.

### Remote Repository Processing

Repomix supports processing remote Git repositories without the need for manual cloning. This feature allows you to
quickly analyze any public Git repository with a single command.

To process a remote repository, use the `--remote` option followed by the repository URL:

```bash
repomix --remote https://github.com/yamadashy/repomix
```

You can also use GitHub's shorthand format:

```bash
repomix --remote yamadashy/repomix
```

You can specify the branch name, tag, or commit hash:

```bash
# Using --remote-branch option
repomix --remote https://github.com/yamadashy/repomix --remote-branch main

# Using branch's URL
repomix --remote https://github.com/yamadashy/repomix/tree/main
```

Or use a specific commit hash:

```bash
# Using --remote-branch option
repomix --remote https://github.com/yamadashy/repomix --remote-branch 935b695

# Using commit's URL
repomix --remote https://github.com/yamadashy/repomix/commit/836abcd7335137228ad77feb28655d85712680f1
```

### Code Compression

The `--compress` option utilizes [Tree-sitter](https://github.com/tree-sitter/tree-sitter) to perform intelligent code extraction, focusing on essential function and class signatures while removing implementation details. This can help reduce token count while retaining important structural information.

```bash
repomix --compress
```

For example, this code:

```typescript
import { ShoppingItem } from './shopping-item';

/**
 * Calculate the total price of shopping items
 */
const calculateTotal = (
  items: ShoppingItem[]
) => {
  let total = 0;
  for (const item of items) {
    total += item.price * item.quantity;
  }
  return total;
}

// Shopping item interface
interface Item {
  name: string;
  price: number;
  quantity: number;
}
```

Will be compressed to:

```typescript
import { ShoppingItem } from './shopping-item';
⋮----
/**
 * Calculate the total price of shopping items
 */
const calculateTotal = (
  items: ShoppingItem[]
) => {
⋮----
// Shopping item interface
interface Item {
  name: string;
  price: number;
  quantity: number;
}
```

> [!NOTE]
> This is an experimental feature that we'll be actively improving based on user feedback and real-world usage

### MCP Server Integration

Repomix supports the [Model Context Protocol (MCP)](https://modelcontextprotocol.io), allowing AI assistants to directly interact with your codebase. When run as an MCP server, Repomix provides tools that enable AI assistants to package local or remote repositories for analysis without requiring manual file preparation.

```bash
repomix --mcp
```

#### Configuring MCP Servers

To use Repomix as an MCP server with AI assistants like Claude, you need to configure the MCP settings:

**For VS Code:**

You can install the Repomix MCP server in VS Code using one of these methods:

1. **Using the Install Badge:**

  [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=repomix&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22repomix%22%2C%22--mcp%22%5D%7D)
  [![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=repomix&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22repomix%22%2C%22--mcp%22%5D%7D&quality=insiders)

2. **Using the Command Line:**

  ```bash
  code --add-mcp '{"name":"repomix","command":"npx","args":["-y","repomix","--mcp"]}'
  ```

  For VS Code Insiders:
  ```bash
  code-insiders --add-mcp '{"name":"repomix","command":"npx","args":["-y","repomix","--mcp"]}'
  ```

**For Cline (VS Code extension):**

Edit the `cline_mcp_settings.json` file:
```json
{
  "mcpServers": {
    "repomix": {
      "command": "npx",
      "args": [
        "-y",
        "repomix",
        "--mcp"
      ]
    }
  }
}
```

**For Cursor:**

In Cursor, add a new MCP server from `Cursor Settings` > `MCP` > `+ Add new global MCP server` with a configuration similar to Cline.

**For Claude Desktop:**

Edit the `claude_desktop_config.json` file with similar configuration to Cline's config.

**Using Docker instead of npx:**

You can use Docker as an alternative to npx for running Repomix as an MCP server:

```json
{
  "mcpServers": {
    "repomix-docker": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "ghcr.io/yamadashy/repomix",
        "--mcp"
      ]
    }
  }
}
```

Once configured, your AI assistant can directly use Repomix's capabilities to analyze codebases without manual file preparation, making code analysis workflows more efficient.

#### Available MCP Tools

When running as an MCP server, Repomix provides the following tools:

1. **pack_codebase**: Package a local code directory into a consolidated XML file for AI analysis
  - Parameters:
    - `directory`: Absolute path to the directory to pack
    - `compress`: (Optional, default: false) Enable Tree-sitter compression to extract essential code signatures and structure while removing implementation details. Reduces token usage by ~70% while preserving semantic meaning. Generally not needed since grep_repomix_output allows incremental content retrieval. Use only when you specifically need the entire codebase content for large repositories.
    - `includePatterns`: (Optional) Specify files to include using fast-glob patterns. Multiple patterns can be comma-separated (e.g., "**/*.{js,ts}", "src/**,docs/**"). Only matching files will be processed.
    - `ignorePatterns`: (Optional) Specify additional files to exclude using fast-glob patterns. Multiple patterns can be comma-separated (e.g., "test/**,*.spec.js", "node_modules/**,dist/**"). These patterns supplement .gitignore and built-in exclusions.
    - `topFilesLength`: (Optional, default: 10) Number of largest files by size to display in the metrics summary for codebase analysis.

2. **pack_remote_repository**: Fetch, clone, and package a GitHub repository into a consolidated XML file for AI analysis
  - Parameters:
    - `remote`: GitHub repository URL or user/repo format (e.g., "yamadashy/repomix", "https://github.com/user/repo", or "https://github.com/user/repo/tree/branch")
    - `compress`: (Optional, default: false) Enable Tree-sitter compression to extract essential code signatures and structure while removing implementation details. Reduces token usage by ~70% while preserving semantic meaning. Generally not needed since grep_repomix_output allows incremental content retrieval. Use only when you specifically need the entire codebase content for large repositories.
    - `includePatterns`: (Optional) Specify files to include using fast-glob patterns. Multiple patterns can be comma-separated (e.g., "**/*.{js,ts}", "src/**,docs/**"). Only matching files will be processed.
    - `ignorePatterns`: (Optional) Specify additional files to exclude using fast-glob patterns. Multiple patterns can be comma-separated (e.g., "test/**,*.spec.js", "node_modules/**,dist/**"). These patterns supplement .gitignore and built-in exclusions.
    - `topFilesLength`: (Optional, default: 10) Number of largest files by size to display in the metrics summary for codebase analysis.

3. **read_repomix_output**: Read the contents of a Repomix-generated output file. Supports partial reading with line range specification for large files.
  - Parameters:
    - `outputId`: ID of the Repomix output file to read
    - `startLine`: (Optional) Starting line number (1-based, inclusive). If not specified, reads from beginning.
    - `endLine`: (Optional) Ending line number (1-based, inclusive). If not specified, reads to end.
  - Features:
    - Specifically designed for web-based environments or sandboxed applications
    - Retrieves the content of previously generated outputs using their ID
    - Provides secure access to packed codebase without requiring file system access
    - Supports partial reading for large files

4. **grep_repomix_output**: Search for patterns in a Repomix output file using grep-like functionality with JavaScript RegExp syntax
  - Parameters:
    - `outputId`: ID of the Repomix output file to search
    - `pattern`: Search pattern (JavaScript RegExp regular expression syntax)
    - `contextLines`: (Optional, default: 0) Number of context lines to show before and after each match. Overridden by beforeLines/afterLines if specified.
    - `beforeLines`: (Optional) Number of context lines to show before each match (like grep -B). Takes precedence over contextLines.
    - `afterLines`: (Optional) Number of context lines to show after each match (like grep -A). Takes precedence over contextLines.
    - `ignoreCase`: (Optional, default: false) Perform case-insensitive matching
  - Features:
    - Uses JavaScript RegExp syntax for powerful pattern matching
    - Supports context lines for better understanding of matches
    - Allows separate control of before/after context lines
    - Case-sensitive and case-insensitive search options

5. **file_system_read_file**: Read a file from the local file system using an absolute path. Includes built-in security validation to detect and prevent access to files containing sensitive information.
  - Parameters:
    - `path`: Absolute path to the file to read
  - Security features:
    - Implements security validation using [Secretlint](https://github.com/secretlint/secretlint)
    - Prevents access to files containing sensitive information (API keys, passwords, secrets)
    - Validates absolute paths to prevent directory traversal attacks

6. **file_system_read_directory**: List the contents of a directory using an absolute path. Returns a formatted list showing files and subdirectories with clear indicators.
  - Parameters:
    - `path`: Absolute path to the directory to list
  - Features:
    - Shows files and directories with clear indicators (`[FILE]` or `[DIR]`)
    - Provides safe directory traversal with proper error handling
    - Validates paths and ensures they are absolute
    - Useful for exploring project structure and understanding codebase organization

## ⚙️ Configuration

Create a `repomix.config.json` file in your project root for custom configurations.

```bash
repomix --init
```

Here's an explanation of the configuration options:

| Option                           | Description                                                                                                                  | Default                |
|----------------------------------|------------------------------------------------------------------------------------------------------------------------------|------------------------|
| `input.maxFileSize`              | Maximum file size in bytes to process. Files larger than this will be skipped                                                | `50000000`            |
| `output.filePath`                | The name of the output file                                                                                                  | `"repomix-output.xml"` |
| `output.style`                   | The style of the output (`xml`, `markdown`, `plain`)                                                                         | `"xml"`                |
| `output.parsableStyle`           | Whether to escape the output based on the chosen style schema. Note that this can increase token count.                      | `false`                |
| `output.compress`                | Whether to perform intelligent code extraction to reduce token count                                                         | `false`                |
| `output.headerText`              | Custom text to include in the file header                                                                                    | `null`                 |
| `output.instructionFilePath`     | Path to a file containing detailed custom instructions                                                                       | `null`                 |
| `output.fileSummary`             | Whether to include a summary section at the beginning of the output                                                          | `true`                 |
| `output.directoryStructure`      | Whether to include the directory structure in the output                                                                     | `true`                 |
| `output.files`                   | Whether to include file contents in the output                                                                               | `true`                 |
| `output.removeComments`          | Whether to remove comments from supported file types                                                                         | `false`                |
| `output.removeEmptyLines`        | Whether to remove empty lines from the output                                                                                | `false`                |
| `output.showLineNumbers`         | Whether to add line numbers to each line in the output                                                                       | `false`                |
| `output.copyToClipboard`         | Whether to copy the output to system clipboard in addition to saving the file                                                | `false`                |
| `output.topFilesLength`          | Number of top files to display in the summary. If set to 0, no summary will be displayed                                     | `5`                    |
| `output.includeEmptyDirectories` | Whether to include empty directories in the repository structure                                                             | `false`                |
| `output.git.sortByChanges`       | Whether to sort files by git change count (files with more changes appear at the bottom)                                     | `true`                 |
| `output.git.sortByChangesMaxCommits` | Maximum number of commits to analyze for git changes                                                                     | `100`                  |
| `output.git.includeDiffs`       | Whether to include git diffs in the output (includes both work tree and staged changes separately)                          | `false`                |
| `include`                        | Patterns of files to include (using [glob patterns](https://github.com/mrmlnc/fast-glob?tab=readme-ov-file#pattern-syntax))  | `[]`                   |
| `ignore.useGitignore`            | Whether to use patterns from the project's `.gitignore` file                                                                 | `true`                 |
| `ignore.useDefaultPatterns`      | Whether to use default ignore patterns                                                                                       | `true`                 |
| `ignore.customPatterns`          | Additional patterns to ignore (using [glob patterns](https://github.com/mrmlnc/fast-glob?tab=readme-ov-file#pattern-syntax)) | `[]`                   |
| `security.enableSecurityCheck`   | Whether to perform security checks on files                                                                                  | `true`                 |
| `tokenCount.encoding`            | Token count encoding used by OpenAI's [tiktoken](https://github.com/openai/tiktoken) tokenizer (e.g., `o200k_base` for GPT-4o, `cl100k_base` for GPT-4/3.5). See [tiktoken model.py](https://github.com/openai/tiktoken/blob/main/tiktoken/model.py#L24) for encoding details. | `"o200k_base"`         |

The configuration file supports [JSON5](https://json5.org/) syntax, which allows:
- Comments (both single-line and multi-line)
- Trailing commas in objects and arrays
- Unquoted property names
- More relaxed string syntax

Example configuration:

```json5
{
  "input": {
    "maxFileSize": 50000000
  },
  "output": {
    "filePath": "repomix-output.xml",
    "style": "xml",
    "parsableStyle": false,
    "compress": false,
    "headerText": "Custom header information for the packed file.",
    "fileSummary": true,
    "directoryStructure": true,
    "files": true,
    "removeComments": false,
    "removeEmptyLines": false,
    "topFilesLength": 5,
    "showLineNumbers": false,
    "copyToClipboard": false,
    "includeEmptyDirectories": false,
    "git": {
      "sortByChanges": true,
      "sortByChangesMaxCommits": 100,
      "includeDiffs": false
    }
  },
  "include": ["**/*"],
  "ignore": {
    "useGitignore": true,
    "useDefaultPatterns": true,
    // Patterns can also be specified in .repomixignore
    "customPatterns": [
      "additional-folder",
      "**/*.log"
    ],
  },
  "security": {
    "enableSecurityCheck": true
  },
  "tokenCount": {
    "encoding": "o200k_base"
  }
}
```

### Global Configuration

To create a global configuration file:

```bash
repomix --init --global
```

The global configuration file will be created in:

- Windows: `%LOCALAPPDATA%\Repomix\repomix.config.json`
- macOS/Linux: `$XDG_CONFIG_HOME/repomix/repomix.config.json` or `~/.config/repomix/repomix.config.json`

Note: Local configuration (if present) takes precedence over global configuration.

### Include and Ignore

#### Include Patterns

Repomix now supports specifying files to include
using [glob patterns](https://github.com/mrmlnc/fast-glob?tab=readme-ov-file#pattern-syntax). This allows for more
flexible and powerful file selection:

- Use `**/*.js` to include all JavaScript files in any directory
- Use `src/**/*` to include all files within the `src` directory and its subdirectories
- Combine multiple patterns like `["src/**/*.js", "**/*.md"]` to include JavaScript files in `src` and all Markdown
  files

#### Ignore Patterns

Repomix offers multiple methods to set ignore patterns for excluding specific files or directories during the packing
process:

- **.gitignore**: By default, patterns listed in your project's `.gitignore` files and `.git/info/exclude` are used. This behavior can be controlled with the `ignore.useGitignore` setting or the `--no-gitignore` cli option.
- **Default patterns**: Repomix includes a default list of commonly excluded files and directories (e.g., node_modules,
  .git, binary files). This feature can be controlled with the `ignore.useDefaultPatterns` setting or the `--no-default-patterns` cli option. Please
  see [defaultIgnore.ts](src/config/defaultIgnore.ts) for more details.
- **.repomixignore**: You can create a `.repomixignore` file in your project root to define Repomix-specific ignore
  patterns. This file follows the same format as `.gitignore`.
- **Custom patterns**: Additional ignore patterns can be specified using the `ignore.customPatterns` option in the
  configuration file. You can overwrite this setting with the `-i, --ignore` command line option.

Priority Order (from highest to lowest):

1. Custom patterns `ignore.customPatterns`
2. `.repomixignore`
3. `.gitignore` and `.git/info/exclude` (if `ignore.useGitignore` is true and `--no-gitignore` is not used)
4. Default patterns (if `ignore.useDefaultPatterns` is true and `--no-default-patterns` is not used)

This approach allows for flexible file exclusion configuration based on your project's needs. It helps optimize the size
of the generated pack file by ensuring the exclusion of security-sensitive files and large binary files, while
preventing the leakage of confidential information.

Note: Binary files are not included in the packed output by default, but their paths are listed in the "Repository
Structure" section of the output file. This provides a complete overview of the repository structure while keeping the
packed file efficient and text-based.

### Custom Instruction

The `output.instructionFilePath` option allows you to specify a separate file containing detailed instructions or
context about your project. This allows AI systems to understand the specific context and requirements of your project,
potentially leading to more relevant and tailored analysis or suggestions.

Here's an example of how you might use this feature:

1. Create a file named `repomix-instruction.md` in your project root:

```markdown
# Coding Guidelines

- Follow the Airbnb JavaScript Style Guide
- Suggest splitting files into smaller, focused units when appropriate
- Add comments for non-obvious logic. Keep all text in English
- All new features should have corresponding unit tests

# Generate Comprehensive Output

- Include all content without abbreviation, unless specified otherwise
- Optimize for handling large codebases while maintaining output quality
```

2. In your `repomix.config.json`, add the `instructionFilePath` option:

```json5
{
  "output": {
    "instructionFilePath": "repomix-instruction.md",
    // other options...
  }
}
```

When Repomix generates the output, it will include the contents of `repomix-instruction.md` in a dedicated section.

Note: The instruction content is appended at the end of the output file. This placement can be particularly effective
for AI systems. For those interested in understanding why this might be beneficial, Anthropic provides some insights in
their documentation:  
https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/long-context-tips

> Put long-form data at the top: Place your long documents and inputs (~20K+ tokens) near the top of your prompt, above
> your query, instructions, and examples. This can significantly improve Claude's performance across all models.
> Queries at the end can improve response quality by up to 30% in tests, especially with complex, multi-document inputs.

### Comment Removal

When `output.removeComments` is set to `true`, Repomix will attempt to remove comments from supported file types. This
feature can help reduce the size of the output file and focus on the essential code content.

Supported languages include:  
HTML, CSS, JavaScript, TypeScript, Vue, Svelte, Python, PHP, Ruby, C, C#, Java, Go, Rust, Swift, Kotlin, Dart, Shell,
and YAML.

Note: The comment removal process is conservative to avoid accidentally removing code. In complex cases, some comments
might be retained.

## 🔍 Security Check

Repomix includes a security check feature that uses [Secretlint](https://github.com/secretlint/secretlint) to detect
potentially sensitive information in your files. This feature helps you identify possible security risks before sharing
your packed repository.

The security check results will be displayed in the CLI output after the packing process is complete. If any suspicious
files are detected, you'll see a list of these files along with a warning message.

Example output:

```
🔍 Security Check:
──────────────────
2 suspicious file(s) detected:
1. src/utils/test.txt
2. tests/utils/secretLintUtils.test.ts

Please review these files for potentially sensitive information.
```

By default, Repomix's security check feature is enabled. You can disable it by setting `security.enableSecurityCheck` to
`false` in your configuration file:

```json
{
  "security": {
    "enableSecurityCheck": false
  }
}
```

Or using the `--no-security-check` command line option:

```bash
repomix --no-security-check
```

> [!NOTE]
> Disabling security checks may expose sensitive information. Use this option with caution and only when necessary, such
> as when working with test files or documentation that contains example credentials.

## 🤖 Using Repomix with GitHub Actions

You can also use Repomix in your GitHub Actions workflows. This is useful for automating the process of packing your codebase for AI analysis.

Basic usage:

```yaml
- name: Pack repository with Repomix
  uses: yamadashy/repomix/.github/actions/repomix@main
  with:
    output: repomix-output.xml
    style: xml
```

Use `--style` to generate output in different formats:

```yaml
- name: Pack repository with Repomix
  uses: yamadashy/repomix/.github/actions/repomix@main
  with:
    output: repomix-output.md
    style: markdown
```

Pack specific directories with compression:

```yaml
- name: Pack repository with Repomix
  uses: yamadashy/repomix/.github/actions/repomix@main
  with:
    directories: src tests
    include: "**/*.ts,**/*.md"
    ignore: "**/*.test.ts"
    output: repomix-output.txt
    compress: true
```

Upload the output file as an artifact:

```yaml
- name: Pack repository with Repomix
  uses: yamadashy/repomix/.github/actions/repomix@main
  with:
    directories: src
    output: repomix-output.txt
    compress: true

- name: Upload Repomix output
  uses: actions/upload-artifact@v4
  with:
    name: repomix-output
    path: repomix-output.txt
```

Complete workflow example:

```yaml
name: Pack repository with Repomix

on:
  workflow_dispatch:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  pack-repo:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Pack repository with Repomix
        uses: yamadashy/repomix/.github/actions/repomix@main
        with:
          output: repomix-output.xml

      - name: Upload Repomix output
        uses: actions/upload-artifact@v4
        with:
          name: repomix-output.xml
          path: repomix-output.xml
          retention-days: 30
```

See the complete workflow example [here](https://github.com/yamadashy/repomix/blob/main/.github/workflows/pack-repository.yml).

### Action Inputs

| Name | Description | Default |
|------|-------------|---------|
| `directories` | Space-separated list of directories to process (e.g., `src tests docs`) | `.` |
| `include` | Comma-separated glob patterns to include files (e.g., `**/*.ts,**/*.md`) | `""` |
| `ignore` | Comma-separated glob patterns to ignore files (e.g., `**/*.test.ts,**/node_modules/**`) | `""` |
| `output` | Relative path for the packed file (extension determines format: `.txt`, `.md`, `.xml`) | `repomix-output.xml` |
| `compress` | Enable smart compression to reduce output size by pruning implementation details | `true` |
| `style` | Output style (`xml`, `markdown`, `plain`) | `xml` |
| `additional-args` | Extra raw arguments for the repomix CLI (e.g., `--no-file-summary --no-security-check`) | `""` |
| `repomix-version` | Version of the npm package to install (supports semver ranges, tags, or specific versions like `0.2.25`) | `latest` |

### Action Outputs

| Name | Description |
|------|-------------|
| `output_file` | Path to the generated output file. Can be used in subsequent steps for artifact upload, LLM processing, or other operations. The file contains a formatted representation of your codebase based on the specified options. |

## 📚 Using Repomix as a Library

In addition to using Repomix as a CLI tool, you can also use it as a library in your Node.js applications.

### Installation

```bash
npm install repomix
```

### Basic Usage

```javascript
import { runCli, type CliOptions } from 'repomix';

// Process current directory with custom options
async function packProject() {
  const options = {
    output: 'output.xml',
    style: 'xml',
    compress: true,
    quiet: true
  } as CliOptions;
  
  const result = await runCli(['.'], process.cwd(), options);
  return result.packResult;
}
```

### Process Remote Repository

```javascript
import { runCli, type CliOptions } from 'repomix';

// Clone and process a GitHub repo
async function processRemoteRepo(repoUrl) {
  const options = {
    remote: repoUrl,
    output: 'output.xml',
    compress: true
  } as CliOptions;
  
  return await runCli(['.'], process.cwd(), options);
}
```

### Using Core Components

If you need more control, you can use the low-level APIs:

```javascript
import { searchFiles, collectFiles, processFiles, TokenCounter } from 'repomix';

async function analyzeFiles(directory) {
  // Find and collect files
  const { filePaths } = await searchFiles(directory, { /* config */ });
  const rawFiles = await collectFiles(filePaths, directory);
  const processedFiles = await processFiles(rawFiles, { /* config */ });
  
  // Count tokens
  const tokenCounter = new TokenCounter('o200k_base');
  
  // Return analysis results
  return processedFiles.map(file => ({
    path: file.path,
    tokens: tokenCounter.countTokens(file.content)
  }));
}
```

For more examples, check the source code at [website/server/src/remoteRepo.ts](https://github.com/yamadashy/repomix/blob/main/website/server/src/remoteRepo.ts) which demonstrates how repomix.com uses the library.

## 🤝 Contribution

We welcome contributions from the community! To get started, please refer to our [Contributing Guide](CONTRIBUTING.md).

### Contributors

<a href="https://github.com/yamadashy/repomix/graphs/contributors">
  <img alt="contributors" src="https://contrib.rocks/image?repo=yamadashy/repomix"/>
</a>

## 🔒 Privacy Policy

### Repomix CLI Tool

- **Data Collection**: The Repomix CLI tool does **not** collect, transmit, or store any user data, telemetry, or repository information.
- **Network Usage**: Repomix CLI operates fully offline after installation. The only cases where an internet connection is needed are:
  - Installation via npm/yarn.
  - Using the `--remote` flag to process remote repositories.
  - Checking for updates (manually triggered).
- **Security Considerations**: Since all processing is local, Repomix CLI is safe to use with private and internal repositories.

### Repomix Website ([repomix.com](https://repomix.com/))

- **Data Collection**: The Repomix website uses **Google Analytics** to collect usage data, such as page views and user interactions. This helps us understand how the website is used and improve the user experience.

### Liability Disclaimer

Repomix (both the CLI tool and the website) is provided "as is" without any warranties or guarantees.  
We do not take responsibility for how the generated output is used, including but not limited to its accuracy, legality, or any potential consequences arising from its use.

## 📜 License

This project is licensed under the [MIT License](LICENSE).

<p align="center">
  &nbsp;&nbsp;&nbsp;
  <a href="#repo-content-pjax-container" target="_blank">
    Back To Top
  </a>
</p>


## /SECURITY.md

# Security Policy

## Reporting a Vulnerability

To securely report a vulnerability, please [open an advisory on GitHub](https://github.com/yamadashy/repomix/security/advisories/new) or report it by sending an email to `koukun0120@gmail.com`.


## /bin/repomix.cjs

```cjs path="/bin/repomix.cjs" 
#!/usr/bin/env node

const nodeVersion = process.versions.node;
const [major] = nodeVersion.split('.').map(Number);

const EXIT_CODES = {
  SUCCESS: 0,
  ERROR: 1,
};

if (major < 16) {
  console.error(`Repomix requires Node.js version 16 or higher. Current version: ${nodeVersion}\n`);
  process.exit(EXIT_CODES.ERROR);
}

function setupErrorHandlers() {
  process.on('uncaughtException', (error) => {
    console.error('Uncaught Exception:', error);
    process.exit(EXIT_CODES.ERROR);
  });

  process.on('unhandledRejection', (reason) => {
    console.error('Unhandled Promise Rejection:', reason);
    process.exit(EXIT_CODES.ERROR);
  });

  function shutdown() {
    process.exit(EXIT_CODES.SUCCESS);
  }

  process.on('SIGINT', () => {
    console.log('\nReceived SIGINT. Shutting down...');
    shutdown();
  });
  process.on('SIGTERM', shutdown);
}

(async () => {
  try {
    setupErrorHandlers();

    const { run } = await import('../lib/cli/cliRun.js');
    await run();
  } catch (error) {
    if (error instanceof Error) {
      console.error('Fatal Error:', {
        name: error.name,
        message: error.message,
        stack: error.stack,
      });
    } else {
      console.error('Fatal Error:', error);
    }

    process.exit(EXIT_CODES.ERROR);
  }
})();

```

## /biome.json

```json path="/biome.json" 
{
  "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
  "files": {
    "include": [
      "./bin/**",
      "./src/**",
      "./tests/**",
      "./website/**",
      "./browser/**",
      "./.devcontainer/**",
      "./.github/**",
      "package.json",
      "biome.json",
      ".secretlintrc.json",
      "tsconfig.json",
      "tsconfig.build.json",
      "vite.config.ts",
      "repomix.config.json"
    ],
    "ignore": [
      "website/client/.vitepress/.temp",
      "website/client/.vitepress/dist",
      "website/client/.vitepress/cache",
      "website/server/dist",
      "browser/dist",
      "browser/packages"
    ]
  },
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  },
  "formatter": {
    "enabled": true,
    "formatWithErrors": false,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 120
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "trailingCommas": "all",
      "semicolons": "always"
    }
  },
  "json": {
    "parser": {
      "allowComments": true,
      "allowTrailingCommas": true
    },
    "formatter": {
      "enabled": false
    }
  }
}

```

## /browser/.gitignore

```gitignore path="/browser/.gitignore" 
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Build output
dist/
packages/

# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# Logs
*.log

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Coverage directory used by tools like istanbul
coverage/

# Temporary folders
tmp/
temp/

```

## /browser/README.md

# Repomix Extension

A browser extension that adds a Repomix button to GitHub repository pages.

## 🚀 Features

- Adds a "Repomix" button to GitHub repository pages
- One-click redirect to Repomix (https://repomix.com)
- Seamlessly integrates with GitHub's UI design
- Works on Chrome, Firefox, and Edge

## 🛠️ Usage

1. Install the browser extension
2. Navigate to any GitHub repository page
3. A "Repomix" button will appear in the page header action area
4. Click the button to open the repository in Repomix

## 💻 Development

### Prerequisites

- Node.js 22 or higher

### Setup

```bash
# Install dependencies
npm install

# Generate icons
npm run generate-icons

# Development mode for Chrome
npm run dev chrome

# Development mode for Firefox
npm run dev firefox

# Development mode for Edge
npm run dev edge
```

### Build

```bash
# Build for all browsers
npm run build-all

# Build for specific browsers
npm run build chrome
npm run build firefox
npm run build edge
```

Built files will be generated in the `dist/` folder.

### Manual Installation

1. Run `npm run build chrome` to build
2. Open `chrome://extensions/` in Chrome
3. Enable "Developer mode"
4. Click "Load unpacked extension"
5. Select the `dist/chrome` folder

## 📝 Technical Specifications

- **Manifest V3** - Latest browser extension specification
- **Content Scripts** - Direct button injection into GitHub pages
- **Internationalization** - English and Japanese support
- **Cross-browser** - Chrome, Firefox, Edge support

## 🔒 Privacy

This extension:
- Does not collect any data
- Does not track user behavior
- Only accesses github.com
- Requires minimal permissions


## /browser/app/_locales/de/detailed-description.txt

Diese Browsererweiterung fügt eine praktische "Repomix"-Schaltfläche zu GitHub-Repository-Seiten hinzu, mit der Sie Repositories schnell mit Repomix verpacken und analysieren können - einem leistungsstarken Tool, das Software-Repositories in AI-freundliche Einzeldateien umwandelt.

🛠️ Funktionen:
- Ein-Klick-Zugriff auf Repomix für jedes GitHub-Repository
- Weitere aufregende Funktionen kommen bald - freuen Sie sich auf erweiterte Funktionalität!

🚀 Verwendung:
1. Erweiterung installieren
2. Zu einem beliebigen GitHub-Repository navigieren
3. Auf die "Repomix"-Schaltfläche im Repository-Header klicken
4. Sie werden zur Repomix-Weboberfläche weitergeleitet
5. Eine verpackte Version des Repositories für AI-Analyse generieren

✨ Was ist Repomix?
Repomix ist ein innovatives Tool, das Ihre gesamte Codebasis in eine einzige, umfassende Datei verpackt, die für AI-Analyse optimiert ist. Es unterstützt mehrere Ausgabeformate (XML, Markdown, Plain Text), enthält Sicherheitsprüfungen zum Ausschluss sensibler Informationen und bietet detaillierte Metriken zu Ihrem Code.

💻 Open Source:
Sowohl diese Erweiterung als auch Repomix selbst sind Open-Source-Projekte. Sie können den Quellcode einsehen, beitragen oder die Erweiterung selbst erstellen.
Weitere Details finden Sie unter:
https://github.com/yamadashy/repomix

🌐 Mehr erfahren:
- Offizielle Website: https://repomix.com
- GitHub-Repository: https://github.com/yamadashy/repomix


## /browser/app/_locales/de/messages.json

```json path="/browser/app/_locales/de/messages.json" 
{
  "appDescription": {
    "message": "Fügt eine Schaltfläche hinzu, um schnell von GitHub-Repositories auf Repomix zuzugreifen",
    "description": "The description of the extension"
  },
  "openWithRepomix": {
    "message": "Mit Repomix öffnen",
    "description": "Button tooltip text for opening repository with Repomix"
  }
}

```

## /browser/app/_locales/en/detailed-description.txt

This browser extension adds a convenient "Repomix" button to GitHub repository pages, allowing you to quickly package and analyze repositories with Repomix - a powerful tool that transforms software repositories into AI-friendly single files.

🛠️ Features:
- One-click access to Repomix for any GitHub repository
- More exciting features coming soon - stay tuned for enhanced functionality!

🚀 How to Use:
1. Install the extension
2. Navigate to any GitHub repository
3. Click the "Repomix" button in the repository header
4. You'll be redirected to Repomix web interface
5. Generate a packaged version of the repository for AI analysis

✨ What is Repomix?
Repomix is an innovative tool that packages your entire codebase into a single, comprehensive file optimized for AI analysis. It supports multiple output formats (XML, Markdown, Plain text), includes security checks to exclude sensitive information, and provides detailed metrics about your code.

💻 Open Source:
Both this extension and Repomix itself are open source projects. You can view the source code, contribute, or build the extension yourself.
For more details, please visit:
https://github.com/yamadashy/repomix

🌐 Learn More:
- Official Website: https://repomix.com
- GitHub Repository: https://github.com/yamadashy/repomix


## /browser/app/_locales/en/messages.json

```json path="/browser/app/_locales/en/messages.json" 
{
  "appDescription": {
    "message": "Add a button to quickly access Repomix from GitHub repositories",
    "description": "The description of the extension"
  },
  "openWithRepomix": {
    "message": "Open with Repomix",
    "description": "Button tooltip text for opening repository with Repomix"
  }
}

```

## /browser/app/_locales/es/detailed-description.txt

Esta extensión de navegador añade un botón "Repomix" conveniente a las páginas de repositorio de GitHub, permitiéndote empaquetar y analizar rápidamente repositorios con Repomix - una herramienta poderosa que transforma repositorios de software en archivos únicos adaptados para IA.

🛠️ Características:
- Acceso con un clic a Repomix para cualquier repositorio de GitHub
- ¡Más características emocionantes llegarán pronto - mantente atento a la funcionalidad mejorada!

🚀 Cómo usar:
1. Instalar la extensión
2. Navegar a cualquier repositorio de GitHub
3. Hacer clic en el botón "Repomix" en el encabezado del repositorio
4. Serás redirigido a la interfaz web de Repomix
5. Generar una versión empaquetada del repositorio para análisis de IA

✨ ¿Qué es Repomix?
Repomix es una herramienta innovadora que empaqueta toda tu base de código en un solo archivo integral optimizado para análisis de IA. Soporta múltiples formatos de salida (XML, Markdown, texto plano), incluye verificaciones de seguridad para excluir información sensible y proporciona métricas detalladas sobre tu código.

💻 Código Abierto:
Tanto esta extensión como Repomix en sí son proyectos de código abierto. Puedes ver el código fuente, contribuir o construir la extensión tú mismo.
Para más detalles, por favor visita:
https://github.com/yamadashy/repomix

🌐 Aprende Más:
- Sitio Web Oficial: https://repomix.com
- Repositorio GitHub: https://github.com/yamadashy/repomix


## /browser/app/_locales/es/messages.json

```json path="/browser/app/_locales/es/messages.json" 
{
  "appDescription": {
    "message": "Añade un botón para acceder rápidamente a Repomix desde repositorios de GitHub",
    "description": "The description of the extension"
  },
  "openWithRepomix": {
    "message": "Abrir con Repomix",
    "description": "Button tooltip text for opening repository with Repomix"
  }
}

```

## /browser/app/_locales/fr/detailed-description.txt

Cette extension de navigateur ajoute un bouton "Repomix" pratique aux pages de dépôt GitHub, vous permettant d'empaqueter et d'analyser rapidement les dépôts avec Repomix - un outil puissant qui transforme les dépôts logiciels en fichiers uniques adaptés à l'IA.

🛠️ Fonctionnalités:
- Accès en un clic à Repomix pour n'importe quel dépôt GitHub
- Plus de fonctionnalités passionnantes arrivent bientôt - restez à l'écoute pour des fonctionnalités améliorées !

🚀 Utilisation:
1. Installer l'extension
2. Naviguer vers n'importe quel dépôt GitHub
3. Cliquer sur le bouton "Repomix" dans l'en-tête du dépôt
4. Vous serez redirigé vers l'interface web Repomix
5. Générer une version empaquetée du dépôt pour l'analyse IA

✨ Qu'est-ce que Repomix ?
Repomix est un outil innovant qui empaquette l'ensemble de votre base de code en un seul fichier complet optimisé pour l'analyse IA. Il prend en charge plusieurs formats de sortie (XML, Markdown, texte brut), inclut des vérifications de sécurité pour exclure les informations sensibles et fournit des métriques détaillées sur votre code.

💻 Open Source:
Cette extension et Repomix lui-même sont des projets open source. Vous pouvez consulter le code source, contribuer ou construire l'extension vous-même.
Pour plus de détails, veuillez visiter:
https://github.com/yamadashy/repomix

🌐 En savoir plus:
- Site officiel: https://repomix.com
- Dépôt GitHub: https://github.com/yamadashy/repomix


## /browser/app/_locales/fr/messages.json

```json path="/browser/app/_locales/fr/messages.json" 
{
  "appDescription": {
    "message": "Ajoute un bouton pour accéder rapidement à Repomix depuis les dépôts GitHub",
    "description": "The description of the extension"
  },
  "openWithRepomix": {
    "message": "Ouvrir avec Repomix",
    "description": "Button tooltip text for opening repository with Repomix"
  }
}

```

## /browser/app/_locales/ja/detailed-description.txt

このブラウザ拡張機能は、GitHubリポジトリページに便利な「Repomix」ボタンを追加し、ソフトウェアリポジトリをAI分析に適した単一ファイルに変換する強力なツールであるRepomixで、リポジトリを素早くパッケージ化・分析できるようにします。

🛠️ 機能:
- あらゆるGitHubリポジトリへのワンクリックアクセス
- さらなるエキサイティングな機能を近日リリース予定 - 機能強化にご期待ください!

🚀 使用方法:
1. 拡張機能をインストール
2. 任意のGitHubリポジトリに移動
3. リポジトリヘッダーの「Repomix」ボタンをクリック
4. Repomixウェブインターフェースにリダイレクトされます
5. AI分析用のリポジトリのパッケージ版を生成

✨ Repomixとは?
Repomixは、コードベース全体をAI分析用に最適化された単一の包括的ファイルにパッケージ化する革新的なツールです。複数の出力フォーマット(XML、Markdown、プレーンテキスト)をサポートし、機密情報を除外するセキュリティチェックを含み、コードに関する詳細なメトリクスを提供します。

💻 オープンソース:
この拡張機能とRepomix自体の両方がオープンソースプロジェクトです。ソースコードを確認したり、貢献したり、自分で拡張機能をビルドしたりできます。
詳細については、以下をご覧ください:
https://github.com/yamadashy/repomix

🌐 詳細情報:
- 公式ウェブサイト: https://repomix.com
- GitHubリポジトリ: https://github.com/yamadashy/repomix


## /browser/app/_locales/ja/messages.json

```json path="/browser/app/_locales/ja/messages.json" 
{
  "appDescription": {
    "message": "GitHubリポジトリからRepomixへ簡単にアクセスするためのボタンを追加します",
    "description": "The description of the extension"
  },
  "openWithRepomix": {
    "message": "Repomixで開く",
    "description": "Button tooltip text for opening repository with Repomix"
  }
}

```

## /browser/app/_locales/ko/detailed-description.txt

이 브라우저 확장 프로그램은 GitHub 리포지토리 페이지에 편리한 "Repomix" 버튼을 추가하여, 소프트웨어 리포지토리를 AI 분석에 적합한 단일 파일로 변환하는 강력한 도구인 Repomix로 리포지토리를 빠르게 패키지화하고 분석할 수 있게 해줍니다.

🛠️ 기능:
- 모든 GitHub 리포지토리에 원클릭으로 Repomix 액세스
- 더 흥미로운 기능들이 곧 출시 예정 - 기능 향상을 기대해 주세요!

🚀 사용 방법:
1. 확장 프로그램 설치
2. 임의의 GitHub 리포지토리로 이동
3. 리포지토리 헤더의 "Repomix" 버튼 클릭
4. Repomix 웹 인터페이스로 리디렉션됩니다
5. AI 분석용 리포지토리의 패키지 버전 생성

✨ Repomix란?
Repomix는 전체 코드베이스를 AI 분석에 최적화된 포괄적인 단일 파일로 패키지화하는 혁신적인 도구입니다. 여러 출력 형식(XML, Markdown, 일반 텍스트)을 지원하고, 민감한 정보를 제외하는 보안 검사를 포함하며, 코드에 대한 자세한 메트릭을 제공합니다.

💻 오픈 소스:
이 확장 프로그램과 Repomix 자체 모두 오픈 소스 프로젝트입니다. 소스 코드를 확인하거나, 기여하거나, 직접 확장 프로그램을 빌드할 수 있습니다.
자세한 내용은 다음을 참조하세요:
https://github.com/yamadashy/repomix

🌐 자세히 알아보기:
- 공식 웹사이트: https://repomix.com
- GitHub 리포지토리: https://github.com/yamadashy/repomix


## /browser/app/_locales/ko/messages.json

```json path="/browser/app/_locales/ko/messages.json" 
{
  "appDescription": {
    "message": "GitHub 리포지토리에서 Repomix에 빠르게 액세스할 수 있는 버튼을 추가합니다",
    "description": "The description of the extension"
  },
  "openWithRepomix": {
    "message": "Repomix로 열기",
    "description": "Button tooltip text for opening repository with Repomix"
  }
}

```

## /browser/app/_locales/pt_BR/detailed-description.txt

Esta extensão de navegador adiciona um botão "Repomix" conveniente às páginas de repositório do GitHub, permitindo que você empacote e analise rapidamente repositórios com Repomix - uma ferramenta poderosa que transforma repositórios de software em arquivos únicos adequados para IA.

🛠️ Recursos:
- Acesso com um clique ao Repomix para qualquer repositório do GitHub
- Mais recursos emocionantes chegando em breve - fique atento para funcionalidades aprimoradas!

🚀 Como usar:
1. Instalar a extensão
2. Navegar para qualquer repositório do GitHub
3. Clicar no botão "Repomix" no cabeçalho do repositório
4. Você será redirecionado para a interface web do Repomix
5. Gerar uma versão empacotada do repositório para análise de IA

✨ O que é Repomix?
Repomix é uma ferramenta inovadora que empacota toda a sua base de código em um único arquivo abrangente otimizado para análise de IA. Suporta múltiplos formatos de saída (XML, Markdown, texto simples), inclui verificações de segurança para excluir informações sensíveis e fornece métricas detalhadas sobre seu código.

💻 Código Aberto:
Tanto esta extensão quanto o próprio Repomix são projetos de código aberto. Você pode visualizar o código fonte, contribuir ou construir a extensão você mesmo.
Para mais detalhes, por favor visite:
https://github.com/yamadashy/repomix

🌐 Saiba Mais:
- Site Oficial: https://repomix.com
- Repositório GitHub: https://github.com/yamadashy/repomix


## /browser/app/_locales/pt_BR/messages.json

```json path="/browser/app/_locales/pt_BR/messages.json" 
{
  "appDescription": {
    "message": "Adiciona um botão para acessar rapidamente o Repomix a partir de repositórios do GitHub",
    "description": "The description of the extension"
  },
  "openWithRepomix": {
    "message": "Abrir com Repomix",
    "description": "Button tooltip text for opening repository with Repomix"
  }
}

```

## /browser/app/_locales/zh_CN/detailed-description.txt

这个浏览器扩展程序为GitHub仓库页面添加了一个便捷的"Repomix"按钮,让您可以快速使用Repomix打包和分析仓库。Repomix是一个强大的工具,可以将软件仓库转换为AI分析友好的单个文件。

🛠️ 功能:
- 一键访问任何GitHub仓库的Repomix
- 更多精彩功能即将推出 - 敬请期待功能增强!

🚀 使用方法:
1. 安装扩展程序
2. 导航到任何GitHub仓库
3. 点击仓库标题中的"Repomix"按钮
4. 您将被重定向到Repomix网页界面
5. 为AI分析生成仓库的打包版本

✨ 什么是Repomix?
Repomix是一个创新工具,可以将您的整个代码库打包成一个针对AI分析优化的综合文件。它支持多种输出格式(XML、Markdown、纯文本),包含安全检查以排除敏感信息,并提供有关代码的详细指标。

💻 开源:
这个扩展程序和Repomix本身都是开源项目。您可以查看源代码、贡献代码或自己构建扩展程序。
更多详情,请访问:
https://github.com/yamadashy/repomix

🌐 了解更多:
- 官方网站: https://repomix.com
- GitHub仓库: https://github.com/yamadashy/repomix


## /browser/app/_locales/zh_CN/messages.json

```json path="/browser/app/_locales/zh_CN/messages.json" 
{
  "appDescription": {
    "message": "为GitHub仓库添加一个按钮,快速访问Repomix",
    "description": "The description of the extension"
  },
  "openWithRepomix": {
    "message": "使用Repomix打开",
    "description": "Button tooltip text for opening repository with Repomix"
  }
}

```

## /browser/app/_locales/zh_TW/detailed-description.txt

這個瀏覽器擴充功能為GitHub儲存庫頁面添加了一個便捷的「Repomix」按鈕,讓您可以快速使用Repomix打包和分析儲存庫。Repomix是一個強大的工具,可以將軟體儲存庫轉換為AI分析友好的單一檔案。

🛠️ 功能:
- 一鍵存取任何GitHub儲存庫的Repomix
- 更多精彩功能即將推出 - 敬請期待功能增強!

🚀 使用方法:
1. 安裝擴充功能
2. 導航到任何GitHub儲存庫
3. 點擊儲存庫標題中的「Repomix」按鈕
4. 您將被重新導向到Repomix網頁介面
5. 為AI分析生成儲存庫的打包版本

✨ 什麼是Repomix?
Repomix是一個創新工具,可以將您的整個程式碼庫打包成一個針對AI分析最佳化的綜合檔案。它支援多種輸出格式(XML、Markdown、純文字),包含安全檢查以排除敏感資訊,並提供有關程式碼的詳細指標。

💻 開源:
這個擴充功能和Repomix本身都是開源專案。您可以查看原始碼、貢獻程式碼或自己建置擴充功能。
更多詳情,請造訪:
https://github.com/yamadashy/repomix

🌐 了解更多:
- 官方網站: https://repomix.com
- GitHub儲存庫: https://github.com/yamadashy/repomix


## /browser/app/_locales/zh_TW/messages.json

```json path="/browser/app/_locales/zh_TW/messages.json" 
{
  "appDescription": {
    "message": "為GitHub儲存庫添加一個按鈕,快速存取Repomix",
    "description": "The description of the extension"
  },
  "openWithRepomix": {
    "message": "使用Repomix開啟",
    "description": "Button tooltip text for opening repository with Repomix"
  }
}

```

## /browser/app/images/icon-128.png

Binary file available at https://raw.githubusercontent.com/yamadashy/repomix/refs/heads/main/browser/app/images/icon-128.png

## /browser/app/images/icon-16.png

Binary file available at https://raw.githubusercontent.com/yamadashy/repomix/refs/heads/main/browser/app/images/icon-16.png

## /browser/app/images/icon-19.png

Binary file available at https://raw.githubusercontent.com/yamadashy/repomix/refs/heads/main/browser/app/images/icon-19.png

## /browser/app/images/icon-32.png

Binary file available at https://raw.githubusercontent.com/yamadashy/repomix/refs/heads/main/browser/app/images/icon-32.png

## /browser/app/images/icon-38.png

Binary file available at https://raw.githubusercontent.com/yamadashy/repomix/refs/heads/main/browser/app/images/icon-38.png

## /browser/app/images/icon-48.png

Binary file available at https://raw.githubusercontent.com/yamadashy/repomix/refs/heads/main/browser/app/images/icon-48.png

## /browser/app/images/icon-64.png

Binary file available at https://raw.githubusercontent.com/yamadashy/repomix/refs/heads/main/browser/app/images/icon-64.png

## /browser/app/images/icon.svg

```svg path="/browser/app/images/icon.svg" 
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="96.259 93.171 300 300" xmlns:bx="https://boxy-svg.com">
  <g id="group-1" transform="matrix(1.160932, 0, 0, 1.160932, 97.635941, 94.725143)" style="">
    <path style="fill-rule: nonzero; fill-opacity: 1; stroke-width: 2; fill: rgb(234, 127, 58);" d="M 128.03 -1.486 L 21.879 65.349 L 21.848 190.25 L 127.979 256.927 L 234.2 190.27 L 234.197 65.463 L 128.03 -1.486 Z M 208.832 70.323 L 127.984 121.129 L 47.173 70.323 L 128.144 19.57 L 208.832 70.323 Z M 39.669 86.367 L 119.188 136.415 L 119.255 230.529 L 39.637 180.386 L 39.669 86.367 Z M 136.896 230.506 L 136.887 136.575 L 216.469 86.192 L 216.417 180.46 L 136.896 230.506 Z M 136.622 230.849"/>
  </g>
</svg>

```

## /browser/app/manifest.json

```json path="/browser/app/manifest.json" 
{
  "name": "Repomix",
  "short_name": "repomix",
  "version": "1.0.0",
  "manifest_version": 3,
  "description": "__MSG_appDescription__",
  "default_locale": "en",
  "icons": {
    "16": "images/icon-16.png",
    "19": "images/icon-19.png",
    "32": "images/icon-32.png",
    "38": "images/icon-38.png",
    "48": "images/icon-48.png",
    "64": "images/icon-64.png",
    "128": "images/icon-128.png"
  },
  "minimum_chrome_version": "88.0",
  "content_scripts": [
    {
      "matches": [
        "https://github.com/*"
      ],
      "css": [
        "styles/content.css"
      ],
      "js": [
        "scripts/content.js"
      ],
      "run_at": "document_start",
      "all_frames": true
    }
  ],
  "background": {
    "service_worker": "scripts/background.js"
  },
  "permissions": [
    "scripting"
  ],
  "web_accessible_resources": [
    {
      "resources": [
        "images/icon-16.png",
        "images/icon-19.png",
        "images/icon-32.png",
        "images/icon-38.png",
        "images/icon-48.png",
        "images/icon-64.png",
        "images/icon-128.png"
      ],
      "matches": ["https://github.com/*"]
    }
  ],
  "host_permissions": [
    "https://github.com/*"
  ],
  "__firefox__background": {
    "scripts": [
      "scripts/background.js"
    ]
  },
  "__firefox__browser_specific_settings": {
    "gecko": {
      "id": "{3AB97897-F299-4DBC-8084-A92813FE2685}",
      "strict_min_version": "102.0"
    }
  }
}

```

## /browser/app/scripts/background.ts

```ts path="/browser/app/scripts/background.ts" 
const injectContentToTab = async (tab: chrome.tabs.Tab): Promise<void> => {
  // Skip if URL is undefined
  if (!tab.url) {
    return;
  }

  // Skip if tab is discarded
  if (tab.discarded) {
    return;
  }

  // Skip if tab ID is undefined
  if (tab.id === undefined) {
    return;
  }

  // Skip if not a GitHub URL
  if (!tab.url.startsWith('https://github.com/')) {
    return;
  }

  try {
    const manifest = chrome.runtime.getManifest();

    // Inject CSS
    if (manifest.content_scripts?.[0]?.css) {
      await chrome.scripting.insertCSS({
        target: { tabId: tab.id },
        files: manifest.content_scripts[0].css,
      });
    }

    // Inject JavaScript
    if (manifest.content_scripts?.[0]?.js) {
      await chrome.scripting.executeScript({
        target: { tabId: tab.id },
        files: manifest.content_scripts[0].js,
      });
    }
  } catch (error) {
    console.error('Error injecting content script:', error);
  }
};

// Update extension content for tabs
chrome.tabs.query({}, async (tabs: chrome.tabs.Tab[]) => {
  for (const tab of tabs) {
    try {
      await injectContentToTab(tab);
    } catch (e) {
      console.error(e);
    }
  }
});

```

## /browser/app/scripts/content.ts

```ts path="/browser/app/scripts/content.ts" 
interface RepositoryInfo {
  owner: string;
  repo: string;
  url: string;
}

interface RepomixButtonOptions {
  text: string;
  href: string;
  iconSrc?: string;
}

// Constants
const BUTTON_CLASS = 'repomix-button';
const ICON_SIZE = 16;
const REPOMIX_BASE_URL = 'https://repomix.com';
const BUTTON_TEXT = 'Repomix';
const DEFAULT_ICON_PATH = 'images/icon-64.png';

// Button functions
function isRepomixButtonAlreadyExists(): boolean {
  return document.querySelector(`.${BUTTON_CLASS}`) !== null;
}

function createRepomixButton(options: RepomixButtonOptions): HTMLElement {
  const container = document.createElement('li');

  const button = document.createElement('a');
  button.className = `${BUTTON_CLASS} btn-sm btn BtnGroup-item`;
  button.href = options.href;
  button.target = '_blank';
  button.rel = 'noopener noreferrer';
  button.title = chrome.i18n.getMessage('openWithRepomix');

  // Create octicon container
  const octicon = document.createElement('span');
  octicon.className = 'octicon';
  octicon.setAttribute('aria-hidden', 'true');

  // Use chrome.runtime.getURL for the icon
  const iconSrc = options.iconSrc || chrome.runtime.getURL(DEFAULT_ICON_PATH);
  octicon.innerHTML = `<img src="${iconSrc}" width="${ICON_SIZE}" height="${ICON_SIZE}" alt="Repomix">`;

  button.appendChild(octicon);

  // Add button text
  const textSpan = document.createElement('span');
  textSpan.textContent = options.text;
  button.appendChild(textSpan);

  container.appendChild(button);
  return container;
}

// GitHub functions
function extractRepositoryInfo(): RepositoryInfo | null {
  const pathMatch = window.location.pathname.match(/^\/([^/]+)\/([^/]+)/);
  if (!pathMatch) {
    return null;
  }

  const [, owner, repo] = pathMatch;
  return {
    owner,
    repo,
    url: `https://github.com/${owner}/${repo}`,
  };
}

function findNavigationContainer(): Element | null {
  return document.querySelector('ul.pagehead-actions');
}

function isRepositoryPage(): boolean {
  // Check if we're on a repository page (not user profile, organization, etc.)
  const pathParts = window.location.pathname.split('/').filter(Boolean);
  return pathParts.length >= 2 && !pathParts[0].startsWith('@');
}

// Main integration functions
function addRepomixButton(): void {
  try {
    // Check if button already exists
    if (isRepomixButtonAlreadyExists()) {
      return;
    }

    // Check if we're on a repository page
    if (!isRepositoryPage()) {
      return;
    }

    // Get repository information
    const repoInfo = extractRepositoryInfo();
    if (!repoInfo) {
      return;
    }

    // Find navigation container
    const navContainer = findNavigationContainer();
    if (!navContainer) {
      return;
    }

    // Create Repomix URL
    const repomixUrl = `${REPOMIX_BASE_URL}/?repo=${encodeURIComponent(repoInfo.url)}`;

    // Create button
    const buttonContainer = createRepomixButton({
      text: BUTTON_TEXT,
      href: repomixUrl,
    });

    // Insert button at the beginning (left side)
    navContainer.prepend(buttonContainer);

    console.log(`Repomix button added for ${repoInfo.owner}/${repoInfo.repo}`);
  } catch (error) {
    console.error('Error adding Repomix button:', error);
  }
}

function observePageChanges(): void {
  // Observe changes to handle GitHub's dynamic navigation
  const observer = new MutationObserver(() => {
    addRepomixButton();
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true,
  });

  // Also listen for popstate events (back/forward navigation)
  window.addEventListener('popstate', () => {
    setTimeout(() => addRepomixButton(), 100);
  });
}

function initRepomixIntegration(): void {
  try {
    addRepomixButton();
    observePageChanges();
  } catch (error) {
    console.error('Error initializing Repomix integration:', error);
  }
}

// Initialize when DOM is ready
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', () => initRepomixIntegration());
} else {
  initRepomixIntegration();
}

```

## /browser/app/styles/content.css

```css path="/browser/app/styles/content.css" 
.repomix-button.btn-sm.btn.BtnGroup-item {
  display: inline-flex;
  align-items: center;
  gap: 0px;
}

.repomix-button .octicon {
  display: inline-flex;
  align-items: center;
}

.repomix-button .octicon img {
  vertical-align: text-bottom;
}

```

## /browser/package.json

```json path="/browser/package.json" 
{
  "private": true,
  "name": "repomix",
  "description": "A browser extension that adds a Repomix button to GitHub repositories",
  "scripts": {
    "dev": "webextension-toolbox dev",
    "build": "webextension-toolbox build",
    "build-all": "npm run build chrome && npm run build firefox && npm run build edge",
    "generate-icons": "tsx scripts/generate-icons.ts",
    "lint": "npm run lint-tsc",
    "lint-tsc": "tsc --noEmit",
    "test": "vitest",
    "archive": "git archive HEAD -o storage/source.zip"
  },
  "keywords": [
    "chrome",
    "extension",
    "firefox",
    "addon",
    "repomix",
    "github",
    "documentation"
  ],
  "author": "yamadashy",
  "license": "MIT",
  "devDependencies": {
    "@biomejs/biome": "^1.9.4",
    "@secretlint/secretlint-rule-preset-recommend": "^9.3.1",
    "@types/chrome": "^0.0.323",
    "@types/node": "^22.10.2",
    "@types/webextension-polyfill": "^0.10.7",
    "@webextension-toolbox/webextension-toolbox": "^7.1.1",
    "secretlint": "^9.3.1",
    "sharp": "^0.34.1",
    "tsx": "^4.19.2",
    "typescript": "^5.8.3"
  },
  "browserslist": [
    "last 2 versions, not dead, > 0.2%"
  ],
  "engines": {
    "node": ">=24.0.1"
  }
}

```

## /browser/promo/Chrome-Webstore-Icon_128x128.png

Binary file available at https://raw.githubusercontent.com/yamadashy/repomix/refs/heads/main/browser/promo/Chrome-Webstore-Icon_128x128.png

## /browser/promo/Promo-Image-Marquee_1400x560.png

Binary file available at https://raw.githubusercontent.com/yamadashy/repomix/refs/heads/main/browser/promo/Promo-Image-Marquee_1400x560.png

## /browser/promo/Promo-Image-Small_440x280.png

Binary file available at https://raw.githubusercontent.com/yamadashy/repomix/refs/heads/main/browser/promo/Promo-Image-Small_440x280.png

## /browser/promo/Screenshot_1280x800.png

Binary file available at https://raw.githubusercontent.com/yamadashy/repomix/refs/heads/main/browser/promo/Screenshot_1280x800.png

## /browser/scripts/generate-icons.ts

```ts path="/browser/scripts/generate-icons.ts" 
import fs from 'node:fs';
import path from 'node:path';
import sharp from 'sharp';

interface IconSize {
  width: number;
  height: number;
}

const ICON_SIZES: readonly number[] = [16, 19, 32, 38, 48, 64, 128] as const;
const INPUT_SVG_PATH = path.join(__dirname, '../app/images/icon.svg');
const OUTPUT_DIR = path.join(__dirname, '../app/images');

/**
 * Ensures the output directory exists
 */
function ensureOutputDirectory(): void {
  if (!fs.existsSync(OUTPUT_DIR)) {
    fs.mkdirSync(OUTPUT_DIR, { recursive: true });
    console.log(`Created output directory: ${OUTPUT_DIR}`);
  }
}

/**
 * Generates a PNG icon of the specified size
 */
async function generateIcon(size: number): Promise<void> {
  try {
    const outputPath = path.join(OUTPUT_DIR, `icon-${size}.png`);

    await sharp(INPUT_SVG_PATH).resize(size, size).png().toFile(outputPath);

    console.log(`✅ Generated ${size}x${size} icon: ${outputPath}`);
  } catch (error) {
    console.error(`❌ Error generating ${size}x${size} icon:`, error);
    throw error;
  }
}

/**
 * Validates that the input SVG file exists
 */
function validateInputFile(): void {
  if (!fs.existsSync(INPUT_SVG_PATH)) {
    throw new Error(`Input SVG file not found: ${INPUT_SVG_PATH}`);
  }
  console.log(`📁 Input SVG: ${INPUT_SVG_PATH}`);
}

/**
 * Main function to generate all icon sizes
 */
async function generateAllIcons(): Promise<void> {
  console.log('🚀 Starting icon generation...');

  try {
    validateInputFile();
    ensureOutputDirectory();

    // Generate all icons in parallel
    const iconPromises = ICON_SIZES.map((size) => generateIcon(size));
    await Promise.all(iconPromises);

    console.log(`🎉 Successfully generated ${ICON_SIZES.length} icons!`);
    console.log(`📂 Output directory: ${OUTPUT_DIR}`);
  } catch (error) {
    console.error('💥 Failed to generate icons:', error);
    process.exit(1);
  }
}

// Execute if this file is run directly
if (require.main === module) {
  void generateAllIcons();
}

export { generateAllIcons, generateIcon, ICON_SIZES };

```

## /browser/tests/repomix-integration.test.ts

```ts path="/browser/tests/repomix-integration.test.ts" 
import { beforeEach, describe, expect, it } from 'vitest';

// Mock DOM environment
Object.defineProperty(window, 'location', {
  value: {
    pathname: '/yamadashy/repomix',
    href: 'https://github.com/yamadashy/repomix',
  },
  writable: true,
});

describe('RepomixIntegration', () => {
  beforeEach(() => {
    // Reset DOM
    document.body.innerHTML = '';

    // Mock GitHub page structure
    const navActions = document.createElement('ul');
    navActions.className = 'pagehead-actions';
    document.body.appendChild(navActions);
  });

  it('should extract repository information correctly', () => {
    // This is a placeholder test since we're testing static methods
    // In a real scenario, we'd need to import and test the actual classes
    const pathMatch = window.location.pathname.match(/^\/([^/]+)\/([^/]+)/);
    expect(pathMatch).toBeTruthy();

    if (pathMatch) {
      const [, owner, repo] = pathMatch;
      expect(owner).toBe('yamadashy');
      expect(repo).toBe('repomix');
    }
  });

  it('should construct correct Repomix URL', () => {
    const repoUrl = 'https://github.com/yamadashy/repomix';
    const expectedUrl = `https://repomix.com/?repo=${encodeURIComponent(repoUrl)}`;

    expect(expectedUrl).toBe('https://repomix.com/?repo=https%3A%2F%2Fgithub.com%2Fyamadashy%2Frepomix');
  });

  it('should find navigation container', () => {
    const navContainer = document.querySelector('ul.pagehead-actions');
    expect(navContainer).toBeTruthy();
  });
});

```

## /browser/tsconfig.json

```json path="/browser/tsconfig.json" 
{
  "compilerOptions": {
    "strict": true,
    "target": "esnext",
    "module": "commonjs",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "lib": [
      "dom",
      "esnext"
    ],
    "allowJs": false,
    "noImplicitAny": true,
    "removeComments": true,
    "skipLibCheck": true,
    "sourceMap": true
  }
}
```

## /browser/vitest.config.ts

```ts path="/browser/vitest.config.ts" 
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'jsdom',
    globals: true,
    setupFiles: [],
    watch: false,
  },
  resolve: {
    extensions: ['.ts', '.js'],
  },
});

```

## /llms-install.md

# Repomix MCP Server Installation Guide

This guide is specifically designed for AI agents like Cline to install and configure the Repomix MCP server for use with LLM applications like Claude Desktop, Cursor, Roo Code, and Cline.

## Overview

Repomix MCP server is a powerful tool that packages local or remote codebases into AI-friendly formats. It allows AI assistants to analyze code efficiently without manual file preparation, optimizing token usage and providing consistent output.

## Prerequisites

Before installation, you need:

1. Node.js 18.0.0 or higher
2. npm (Node Package Manager)

## Installation and Configuration

### Configure MCP Settings

Add the Repomix MCP server configuration to your MCP settings file based on your LLM client:

#### Configuration File Locations

- Cline (VS Code Extension): `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
- Roo Code (VS Code Extension): `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`
- Claude Desktop: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Cursor: `[project root]/.cursor/mcp.json`

Add this configuration to your chosen client's settings file:

```json
{
  "mcpServers": {
    "repomix": {
      "command": "npx",
      "args": [
        "-y",
        "repomix",
        "--mcp"
      ],
      "disabled": false,
      "autoApprove": []
    }
  }
}
```

This configuration uses `npx` to run Repomix directly without requiring a global installation.

## Available MCP Tools

Once configured, you'll have access to these Repomix tools:

### 1. pack_codebase

This tool packages a local code directory into a consolidated XML file for AI analysis. It analyzes the codebase structure, extracts relevant code content, and generates a comprehensive report including metrics, file tree, and formatted code content.

**Parameters:**
- `directory`: (Required) Absolute path to the directory to pack
- `compress`: (Optional, default: false) Enable Tree-sitter compression to extract essential code signatures and structure while removing implementation details. Reduces token usage by ~70% while preserving semantic meaning. Generally not needed since grep_repomix_output allows incremental content retrieval. Use only when you specifically need the entire codebase content for large repositories.
- `includePatterns`: (Optional) Specify files to include using fast-glob patterns. Multiple patterns can be comma-separated (e.g., "**/*.{js,ts}", "src/**,docs/**"). Only matching files will be processed.
- `ignorePatterns`: (Optional) Specify additional files to exclude using fast-glob patterns. Multiple patterns can be comma-separated (e.g., "test/**,*.spec.js", "node_modules/**,dist/**"). These patterns supplement .gitignore and built-in exclusions.
- `topFilesLength`: (Optional, default: 10) Number of largest files by size to display in the metrics summary for codebase analysis.

**Example:**
```json
{
  "directory": "/path/to/your/project",
  "compress": false,
  "includePatterns": "src/**/*.ts,**/*.md",
  "ignorePatterns": "**/*.log,tmp/",
  "topFilesLength": 10
}
```

### 2. pack_remote_repository

This tool fetches, clones, and packages a GitHub repository into a consolidated XML file for AI analysis. It automatically clones the remote repository, analyzes its structure, and generates a comprehensive report.

**Parameters:**
- `remote`: (Required) GitHub repository URL or user/repo format (e.g., "yamadashy/repomix", "https://github.com/user/repo", or "https://github.com/user/repo/tree/branch")
- `compress`: (Optional, default: false) Enable Tree-sitter compression to extract essential code signatures and structure while removing implementation details. Reduces token usage by ~70% while preserving semantic meaning. Generally not needed since grep_repomix_output allows incremental content retrieval. Use only when you specifically need the entire codebase content for large repositories.
- `includePatterns`: (Optional) Specify files to include using fast-glob patterns. Multiple patterns can be comma-separated (e.g., "**/*.{js,ts}", "src/**,docs/**"). Only matching files will be processed.
- `ignorePatterns`: (Optional) Specify additional files to exclude using fast-glob patterns. Multiple patterns can be comma-separated (e.g., "test/**,*.spec.js", "node_modules/**,dist/**"). These patterns supplement .gitignore and built-in exclusions.
- `topFilesLength`: (Optional, default: 10) Number of largest files by size to display in the metrics summary for codebase analysis.

**Example:**
```json
{
  "remote": "yamadashy/repomix",
  "compress": false,
  "includePatterns": "src/**/*.ts,**/*.md",
  "ignorePatterns": "**/*.log,tmp/",
  "topFilesLength": 10
}
```

### 3. read_repomix_output

This tool reads the contents of a Repomix-generated output file. Supports partial reading with line range specification for large files. This tool is designed for environments where direct file system access is limited.

**Parameters:**
- `outputId`: (Required) ID of the Repomix output file to read
- `startLine`: (Optional) Starting line number (1-based, inclusive). If not specified, reads from beginning.
- `endLine`: (Optional) Ending line number (1-based, inclusive). If not specified, reads to end.

**Features:**
- Specifically designed for web-based environments or sandboxed applications
- Retrieves the content of previously generated outputs using their ID
- Provides secure access to packed codebase without requiring file system access
- Supports partial reading for large files

**Example:**
```json
{
  "outputId": "8f7d3b1e2a9c6054",
  "startLine": 100,
  "endLine": 200
}
```

### 4. grep_repomix_output

This tool searches for patterns in a Repomix output file using grep-like functionality with JavaScript RegExp syntax. Returns matching lines with optional context lines around matches.

**Parameters:**
- `outputId`: (Required) ID of the Repomix output file to search
- `pattern`: (Required) Search pattern (JavaScript RegExp regular expression syntax)
- `contextLines`: (Optional, default: 0) Number of context lines to show before and after each match. Overridden by beforeLines/afterLines if specified.
- `beforeLines`: (Optional) Number of context lines to show before each match (like grep -B). Takes precedence over contextLines.
- `afterLines`: (Optional) Number of context lines to show after each match (like grep -A). Takes precedence over contextLines.
- `ignoreCase`: (Optional, default: false) Perform case-insensitive matching

**Features:**
- Uses JavaScript RegExp syntax for powerful pattern matching
- Supports context lines for better understanding of matches
- Allows separate control of before/after context lines
- Case-sensitive and case-insensitive search options

**Example:**
```json
{
  "outputId": "8f7d3b1e2a9c6054",
  "pattern": "function\\s+\\w+\\(",
  "contextLines": 3,
  "ignoreCase": false
}
```

### 5. file_system_read_file

This tool reads a file from the local file system using an absolute path. Includes built-in security validation to detect and prevent access to files containing sensitive information.

**Parameters:**
- `path`: (Required) Absolute path to the file to read

**Security features:**
- Implements security validation using [Secretlint](https://github.com/secretlint/secretlint)
- Prevents access to files containing sensitive information (API keys, passwords, secrets)
- Validates absolute paths to prevent directory traversal attacks

**Example:**
```json
{
  "path": "/absolute/path/to/file.txt"
}
```

### 6. file_system_read_directory

This tool lists the contents of a directory using an absolute path. Returns a formatted list showing files and subdirectories with clear indicators.

**Parameters:**
- `path`: (Required) Absolute path to the directory to list

**Features:**
- Shows files and directories with clear indicators (`[FILE]` or `[DIR]`)
- Provides safe directory traversal with proper error handling
- Validates paths and ensures they are absolute
- Useful for exploring project structure and understanding codebase organization

**Example:**
```json
{
  "path": "/absolute/path/to/directory"
}
```

## Verify Installation

To verify the installation is working:

1. Restart your LLM application (Cline, Claude Desktop, etc.)
2. Test the connection by running a simple command like:
  ```
  Please package the local directory /path/to/project for AI analysis using Repomix.
  ```
  or
  ```
  Please fetch and package the GitHub repository yamadashy/repomix for AI analysis.
  ```

## Usage Examples

Here are some examples of how to use Repomix MCP server with AI assistants:

### Local Codebase Analysis

```
Can you analyze the code in my project at /path/to/project? Please use Repomix to package it first.
```

### Remote Repository Analysis

```
I'd like you to review the code in the GitHub repository username/repo. Please use Repomix to package it first.
```

### Specific File Types Analysis

```
Please package my project at /path/to/project, but only include TypeScript files and markdown documentation.
```

## Troubleshooting

### Common Issues and Solutions

1. **MCP server connection issues**
  - Verify the syntax in your MCP settings file is correct
  - Ensure you have an active internet connection (needed for npx to fetch the package)
  - Check if any other MCP servers are causing conflicts

2. **Packaging failures**
  - Verify the specified directory or repository exists
  - Check if you have sufficient disk space
  - For remote repositories, ensure you have internet connectivity
  - Try with simpler parameters first, then add complexity

3. **JSON parsing errors in configuration**
  - Make sure your MCP settings file is properly formatted
  - Verify all paths use forward slashes, even on Windows
  - Check for any missing commas or brackets in the configuration

## Additional Information

For more detailed information, visit the [Repomix official documentation](https://repomix.com). You can also join the [Discord community](https://discord.gg/wNYzTwZFku) for support and questions.


## /package.json

```json path="/package.json" 
{
  "name": "repomix",
  "version": "0.3.7",
  "description": "A tool to pack repository contents to single file for AI consumption",
  "main": "./lib/index.js",
  "types": "./lib/index.d.ts",
  "exports": {
    ".": {
      "import": {
        "types": "./lib/index.d.ts",
        "default": "./lib/index.js"
      },
      "require": {
        "types": "./lib/index.d.ts",
        "default": "./lib/index.js"
      },
      "default": "./lib/index.js"
    }
  },
  "bin": "./bin/repomix.cjs",
  "scripts": {
    "clean": "rimraf lib",
    "build": "npm run clean && tsc -p tsconfig.build.json --sourceMap --declaration",
    "lint": "npm run lint-biome && npm run lint-ts && npm run lint-secretlint",
    "lint-biome": "biome check --write",
    "lint-ts": "tsc --noEmit",
    "lint-secretlint": "secretlint \"**/*\" --secretlintignore .gitignore",
    "test": "vitest",
    "test-coverage": "vitest run --coverage",
    "repomix": "npm run build && node --trace-warnings bin/repomix.cjs",
    "repomix-src": "npm run repomix -- --include 'src,tests'",
    "repomix-website": "npm run repomix -- --include 'website'",
    "website": "docker compose -f website/compose.yml up --build",
    "website-generate-schema": "tsx website/client/scripts/generateSchema.ts",
    "npm-publish": "npm run npm-publish-check-branch && npm run lint && npm run test-coverage && npm run build && npm publish",
    "npm-publish-check-branch": "git branch --show-current | grep -q '^main{{contextString}}#39; || (echo 'Release is only allowed from the main branch' && exit 1)",
    "npm-release-patch": "npm version patch && npm run npm-publish",
    "npm-release-minor": "npm version minor && npm run npm-publish",
    "npm-release-prerelease": "npm version prerelease && npm run npm-publish"
  },
  "keywords": [
    "repository",
    "generative-ai",
    "ai",
    "llm",
    "source-code",
    "code-analysis",
    "codebase-packer",
    "development-tool",
    "ai-assistant",
    "code-review"
  ],
  "repository": {
    "type": "git",
    "url": "git://github.com/yamadashy/repomix.git"
  },
  "bugs": {
    "url": "https://github.com/yamadashy/repomix/issues"
  },
  "author": "Kazuki Yamada <koukun0120@gmail.com>",
  "homepage": "https://github.com/yamadashy/repomix",
  "license": "MIT",
  "publishConfig": {
    "access": "public"
  },
  "type": "module",
  "dependencies": {
    "@clack/prompts": "^0.10.1",
    "@modelcontextprotocol/sdk": "^1.11.0",
    "@secretlint/core": "^9.3.1",
    "@secretlint/secretlint-rule-preset-recommend": "^9.3.1",
    "cli-spinners": "^2.9.2",
    "clipboardy": "^4.0.0",
    "commander": "^14.0.0",
    "fast-xml-parser": "^5.2.0",
    "git-url-parse": "^16.1.0",
    "globby": "^14.1.0",
    "handlebars": "^4.7.8",
    "iconv-lite": "^0.6.3",
    "istextorbinary": "^9.5.0",
    "jschardet": "^3.1.4",
    "json5": "^2.2.3",
    "log-update": "^6.1.0",
    "minimatch": "^10.0.1",
    "picocolors": "^1.1.1",
    "piscina": "^4.9.2",
    "strip-comments": "^2.0.1",
    "strip-json-comments": "^5.0.1",
    "tiktoken": "^1.0.20",
    "tree-sitter-wasms": "^0.1.12",
    "web-tree-sitter": "^0.24.7",
    "zod": "^3.24.3"
  },
  "devDependencies": {
    "@biomejs/biome": "^1.9.4",
    "@types/node": "^22.14.1",
    "@types/strip-comments": "^2.0.4",
    "@vitest/coverage-v8": "^3.1.1",
    "rimraf": "^6.0.1",
    "secretlint": "^9.3.1",
    "tsx": "^4.19.4",
    "typescript": "^5.8.3",
    "vite": "^5.4.18",
    "vitest": "^3.1.1"
  },
  "engines": {
    "node": ">=18.0.0",
    "yarn": ">=1.22.22"
  }
}

```

## /repomix-instruction.md

# Repomix Project Structure and Overview

This document provides a structural overview of the Repomix project, designed to aid AI code assistants (like Copilot) in understanding the codebase.

Please refer to `README.md` for a complete and up-to-date project overview, and `CONTRIBUTING.md` for implementation guidelines and contribution procedures.

## Project Overview

Repomix is a tool that packs the contents of a software repository into a single file, making it easier for AI systems to analyze and process the codebase. It supports various output formats (plain text, XML, Markdown), ignores files based on configurable patterns, and performs security checks to exclude potentially sensitive information.

## Directory Structure

The project is organized into the following directories:

```
repomix/
├── src/ # Main source code
│   ├── cli/ # Command-line interface logic (argument parsing, command handling, output)
│   ├── config/ # Configuration loading, schema, and defaults
│   ├── core/ # Core logic of Repomix
│   │   ├── file/ # File handling (reading, processing, searching, tree structure generation, git commands)
│   │   ├── metrics/ # Calculating code metrics (character count, token count)
│   │   ├── output/ # Output generation (different styles, headers, etc.)
│   │   ├── packager/ # Orchestrates file collection, processing, output, and clipboard operations.
│   │   ├── security/ # Security checks to exclude sensitive files
│   │   ├── tokenCount/ # Token counting using Tiktoken
│   │   └── tree-sitter/ # Code parsing using Tree-sitter and language-specific queries
│   └── shared/ # Shared utilities and types (error handling, logging, helper functions)
├── tests/ # Unit and integration tests (organized mirroring src/)
│   ├── cli/
│   ├── config/
│   ├── core/
│   ├── integration-tests/
│   ├── shared/
│   └── testing/
└── website/ # Documentation website (VitePress)
    ├── client/      # Client-side code (Vue.js components, styles, configuration)
    │   ├── .vitepress/  # VitePress configuration and theme
    │   │   ├── config/  # Site configuration files (navigation, sidebar, etc.)
    │   │   └── theme/   # Custom theme and styles
    │   ├── components/ # Vue.js components for the website
    │   └── src/        # Markdown files for the documentation in various languages (en, ja, etc.)
    └── server/      # Server-side API (for remote repository processing)
        └── src/       # Server source code (API endpoints, request handling)
```

----------------------------------------------------------------

# Coding Guidelines
- Follow the Airbnb JavaScript Style Guide.
- Split files into smaller, focused units when appropriate:
  - Aim to keep code files under 250 lines. If a file exceeds 250 lines, split it into multiple files based on functionality.
- Add comments to clarify non-obvious logic. **Ensure all comments are written in English.**
- Provide corresponding unit tests for all new features.
- After implementation, verify changes by running:
  ```bash
  npm run lint  # Ensure code style compliance
  npm run test  # Verify all tests pass
  ```

## Dependencies and Testing
- Inject dependencies through a deps object parameter for testability
- Example:
  ```typescript
  export const functionName = async (
    param1: Type1,
    param2: Type2,
    deps = {
      defaultFunction1,
      defaultFunction2,
    }
  ) => {
    // Use deps.defaultFunction1() instead of direct call
  };
  ```
- Mock dependencies by passing test doubles through deps object
- Use vi.mock() only when dependency injection is not feasible

## Generate Comprehensive Output
- Include all content without abbreviation, unless specified otherwise
- Optimize for handling large codebases while maintaining output quality

----------------------------------------------------------------

# GitHub Release Note Guidelines
When writing release notes, please follow these guidelines:

- When referencing issues or PRs, use the gh command to verify the content:
  ```bash
  gh issue view <issue-number>  # For checking issue content
  gh pr view <pr-number>        # For checking PR content
  ```
  This helps ensure accuracy in release note descriptions.

Here are some examples of release notes that follow the guidelines:

v0.2.25
````md
This release brings significant improvements to output formatting and introduces flexible remote repository handling capabilities along with enhanced logging features.

# Improvements ⚡

## Remote Repository Enhancement (#335)
- Added branch/tag parsing directly from repository URLs:
```bash
repomix --remote https://github.com/yamadashy/repomix/tree/0.1.x
```
Functions identically to:
```bash
repomix --remote https://github.com/yamadashy/repomix --remote-branch 0.1.x
```

Special thanks to @huy-trn for implementing this user-friendly feature!

## Enhanced Output Formatting (#328, #329, #330)
- Added "End of Codebase" marker for better clarity in output
- Improved output header accuracy:
  - Better representation of codebase scope
  - Clear indication when using `--include` or `--ignore` options

Special thanks to @gitkenan for adding the "End of Codebase" marker and reporting the header issue!

## Path Pattern Support (#337)
- Added support for special characters in paths:
  - Handles parentheses in include patterns (e.g., `src/(categories)/**/*`)
  - Improved escaping for `[]` and `{}`
  - Essential for Next.js route groups and similar frameworks

Thank you @matheuscoelhomalta for improving path pattern support!

# How to Update

```bash
npm update -g repomix
```

---

As always, if you encounter any issues or have suggestions, please let us know through our GitHub issues or join our [Discord community](https://discord.gg/wNYzTwZFku) for support.
````

v0.2.24
````md
This release significantly enhances configuration flexibility with comprehensive CLI flag support and expands default ignore patterns for better project scaffolding. 

# What's New 🚀

## CLI Flags Revolution (#324)
- New command-line configuration now available.

```
- `--no-gitignore`: Disable .gitignore file usage
- `--no-default-patterns`: Disable default patterns
- `--header-text <text>`: Custom text to include in the file header
- `--instruction-file-path <path>`: Path to a file containing detailed custom instructions
- `--include-empty-directories`: Include empty directories in the output
```

Special recognition to @massdo for driving ecosystem growth.

# Improvements ⚡

## Enhanced Ignore Patterns (#318, #322)
- Expanded default ignores for Rust projects:
  - `target/`, `Cargo.lock`, build artifacts
  - PHP, Ruby, Go, Elixir, Haskell: package manager lock files

To @boralg for helping curate Rust-specific patterns!

# How to Update
```bash
npm update -g repomix
```

---

As always, if you encounter any issues or have suggestions, please let us know through our GitHub issues or join our [Discord community](https://discord.gg/wNYzTwZFku) for support.
````

v0.2.23
````md
This release adds significant performance improvements for large repositories, making Repomix faster and more efficient when needed.

# Improvements ⚡

## Parallel Processing Enhancement (#309)
- Implemented worker threads using [Piscina](https://github.com/piscinajs/piscina) for parallel processing

### Benchmark Results
- `yamadashy.repomix`: No significant change
  - Before: 868.73 millis
  - After: 671.26 millis
- `facebook/react`: 29x faster
  - Before: 123.31 secs
  - After: 4.19 secs
- `vercel/next.js`: 58x faster
  - Before: 17.85 mins
  - After: 17.27 secs

Note: While Repomix is not primarily designed for processing large repositories, and speed is not a primary goal, faster processing can provide a better user experience when working with larger codebases.

# How to Update

```bash
npm update -g repomix
```


---

As always, if you encounter any issues or have suggestions, please let us know through our GitHub issues or join our [Discord community](https://discord.gg/wNYzTwZFku) for support.
````

v0.2.22
````md
This release introduces significant improvements to large file handling and expands the Repomix ecosystem with new tools and community channels.

# Improvements ⚡ 

## Improved Large File Handling (#302)

- Added a file size limit check (50MB) to prevent memory issues
- Graceful error handling for large files with clear user guidance:

Special thanks to @slavashvets for their continued contributions!

# Ecosystem Growth 🤝 

## New VS Code Extension (#300)
A community-created VS Code extension "Repomix Runner" is now available:
- Run Repomix directly from VS Code
- Extension by @massdo: [View on VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=DorianMassoulier.repomix-runner)

Thank you @massdo for bringing Repomix to VS Code and expanding our tooling ecosystem!

## Official Social Media
- Launched official Repomix X (Twitter) account: [@repomix_ai](https://x.com/repomix_ai)
  - Follow for updates, tips, and community highlights

# How to Update

```bash
npm update -g repomix
```

---

Join our growing community on [Discord](https://discord.gg/BF8GxZHE2C) and follow us on [X](https://x.com/repomix_ai) for updates!
````

v0.2.21
````md
This release introduces significant improvements to output formatting and documentation, featuring a new parsable style option for enhanced XML handling.

# What's New 🚀 

## Enhanced Output Style Control (#287)
- Added new `parsableStyle` option for better output handling:
  - Ensures output strictly follows the specification of the chosen format
  - Provides properly escaped XML output with fast-xml-parser
  - Dynamically adjusts markdown code block delimiters to avoid content conflicts
- Available via CLI flag `--parsable-style` or in configuration file

Special thanks to @atollk for their first contribution!

# Documentation 📚

## README Enhancements (#296)
- Updated Homebrew installation documentation to include Linux support

Special thanks to @chenrui333 for their continued contributions!

## Website Multi-Language Support (#293)
- Enhanced multi-language support in [repomix.com](https://repomix.com)

# How to Update

To update to the latest version, run:
```bash
npm update -g repomix
```


---

As always, if you encounter any issues or have suggestions, please let us know through our GitHub issues or join our [Discord community](https://discord.gg/wNYzTwZFku) for support.


## /repomix.config.json

```json path="/repomix.config.json" 
{
  "$schema": "https://repomix.com/schemas/latest/schema.json",
  "input": {
    "maxFileSize": 50000000
  },
  "output": {
    "filePath": "repomix-output.xml",
    "style": "xml",
    "parsableStyle": false,
    "compress": false,
    "headerText": "This repository contains the source code for the Repomix tool.\nRepomix 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",
    "instructionFilePath": "repomix-instruction.md",
    "fileSummary": true,
    "directoryStructure": true,
    "files": true,
    "removeComments": false,
    "removeEmptyLines": false,
    "topFilesLength": 5,
    "showLineNumbers": false,
    "includeEmptyDirectories": true,
    "git": {
      "sortByChanges": true,
      "sortByChangesMaxCommits": 100,
      "includeDiffs": false
    }
  },
  "include": [],
  "ignore": {
    "useGitignore": true,
    "useDefaultPatterns": true,
    // ignore is specified in .repomixignore
    "customPatterns": []
  },
  "security": {
    "enableSecurityCheck": true
  },
  "tokenCount": {
    "encoding": "o200k_base"
  }
}

```

## /src/cli/actions/defaultAction.ts

```ts path="/src/cli/actions/defaultAction.ts" 
import path from 'node:path';
import { loadFileConfig, mergeConfigs } from '../../config/configLoad.js';
import {
  type RepomixConfigCli,
  type RepomixConfigFile,
  type RepomixConfigMerged,
  type RepomixOutputStyle,
  repomixConfigCliSchema,
} from '../../config/configSchema.js';
import { type PackResult, pack } from '../../core/packager.js';
import { rethrowValidationErrorIfZodError } from '../../shared/errorHandle.js';
import { logger } from '../../shared/logger.js';
import { splitPatterns } from '../../shared/patternUtils.js';
import { printCompletion, printSecurityCheck, printSummary, printTopFiles } from '../cliPrint.js';
import { Spinner } from '../cliSpinner.js';
import type { CliOptions } from '../types.js';
import { runMigrationAction } from './migrationAction.js';

export interface DefaultActionRunnerResult {
  packResult: PackResult;
  config: RepomixConfigMerged;
}

export const runDefaultAction = async (
  directories: string[],
  cwd: string,
  cliOptions: CliOptions,
): Promise<DefaultActionRunnerResult> => {
  logger.trace('Loaded CLI options:', cliOptions);

  // Run migration before loading config
  await runMigrationAction(cwd);

  // Load the config file
  const fileConfig: RepomixConfigFile = await loadFileConfig(cwd, cliOptions.config ?? null);
  logger.trace('Loaded file config:', fileConfig);

  // Parse the CLI options into a config
  const cliConfig: RepomixConfigCli = buildCliConfig(cliOptions);
  logger.trace('CLI config:', cliConfig);

  // Merge default, file, and CLI configs
  const config: RepomixConfigMerged = mergeConfigs(cwd, fileConfig, cliConfig);

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

  const targetPaths = directories.map((directory) => path.resolve(cwd, directory));

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

  let packResult: PackResult;

  try {
    packResult = await pack(targetPaths, config, (message) => {
      spinner.update(message);
    });
  } catch (error) {
    spinner.fail('Error during packing');
    throw error;
  }

  spinner.succeed('Packing completed successfully!');
  logger.log('');

  if (config.output.topFilesLength > 0) {
    printTopFiles(packResult.fileCharCounts, packResult.fileTokenCounts, config.output.topFilesLength);
    logger.log('');
  }

  printSecurityCheck(cwd, packResult.suspiciousFilesResults, packResult.suspiciousGitDiffResults, config);
  logger.log('');

  printSummary(packResult, config);
  logger.log('');

  printCompletion();

  return {
    packResult,
    config,
  };
};

/**
 * Builds CLI configuration from command-line options.
 *
 * Note: Due to Commander.js behavior with --no-* flags:
 * - When --no-* flags are used (e.g., --no-file-summary), the options explicitly become false
 * - When no flag is specified, Commander defaults to true (e.g., options.fileSummary === true)
 * - For --no-* flags, we only apply the setting when it's explicitly false to respect config file values
 * - This allows the config file to maintain control unless explicitly overridden by CLI
 */
export const buildCliConfig = (options: CliOptions): RepomixConfigCli => {
  const cliConfig: RepomixConfigCli = {};

  if (options.output) {
    cliConfig.output = { filePath: options.output };
  }
  if (options.include) {
    cliConfig.include = splitPatterns(options.include);
  }
  if (options.ignore) {
    cliConfig.ignore = { customPatterns: splitPatterns(options.ignore) };
  }
  // Only apply gitignore setting if explicitly set to false
  if (options.gitignore === false) {
    cliConfig.ignore = { ...cliConfig.ignore, useGitignore: options.gitignore };
  }
  // Only apply defaultPatterns setting if explicitly set to false
  if (options.defaultPatterns === false) {
    cliConfig.ignore = {
      ...cliConfig.ignore,
      useDefaultPatterns: options.defaultPatterns,
    };
  }
  if (options.topFilesLen !== undefined) {
    cliConfig.output = {
      ...cliConfig.output,
      topFilesLength: options.topFilesLen,
    };
  }
  if (options.outputShowLineNumbers !== undefined) {
    cliConfig.output = {
      ...cliConfig.output,
      showLineNumbers: options.outputShowLineNumbers,
    };
  }
  if (options.copy) {
    cliConfig.output = { ...cliConfig.output, copyToClipboard: options.copy };
  }
  if (options.style) {
    cliConfig.output = {
      ...cliConfig.output,
      style: options.style.toLowerCase() as RepomixOutputStyle,
    };
  }
  if (options.parsableStyle !== undefined) {
    cliConfig.output = {
      ...cliConfig.output,
      parsableStyle: options.parsableStyle,
    };
  }
  if (options.stdout) {
    cliConfig.output = {
      ...cliConfig.output,
      stdout: true,
    };
  }
  // Only apply securityCheck setting if explicitly set to false
  if (options.securityCheck === false) {
    cliConfig.security = { enableSecurityCheck: options.securityCheck };
  }
  // Only apply fileSummary setting if explicitly set to false
  if (options.fileSummary === false) {
    cliConfig.output = {
      ...cliConfig.output,
      fileSummary: false,
    };
  }
  // Only apply directoryStructure setting if explicitly set to false
  if (options.directoryStructure === false) {
    cliConfig.output = {
      ...cliConfig.output,
      directoryStructure: false,
    };
  }
  // Only apply files setting if explicitly set to false
  if (options.files === false) {
    cliConfig.output = {
      ...cliConfig.output,
      files: false,
    };
  }
  if (options.removeComments !== undefined) {
    cliConfig.output = {
      ...cliConfig.output,
      removeComments: options.removeComments,
    };
  }
  if (options.removeEmptyLines !== undefined) {
    cliConfig.output = {
      ...cliConfig.output,
      removeEmptyLines: options.removeEmptyLines,
    };
  }
  if (options.headerText !== undefined) {
    cliConfig.output = { ...cliConfig.output, headerText: options.headerText };
  }

  if (options.compress !== undefined) {
    cliConfig.output = { ...cliConfig.output, compress: options.compress };
  }

  if (options.tokenCountEncoding) {
    cliConfig.tokenCount = { encoding: options.tokenCountEncoding };
  }
  if (options.instructionFilePath) {
    cliConfig.output = {
      ...cliConfig.output,
      instructionFilePath: options.instructionFilePath,
    };
  }
  if (options.includeEmptyDirectories) {
    cliConfig.output = {
      ...cliConfig.output,
      includeEmptyDirectories: options.includeEmptyDirectories,
    };
  }

  // Only apply gitSortByChanges setting if explicitly set to false
  if (options.gitSortByChanges === false) {
    cliConfig.output = {
      ...cliConfig.output,
      git: {
        ...cliConfig.output?.git,
        sortByChanges: false,
      },
    };
  }

  if (options.includeDiffs) {
    cliConfig.output = {
      ...cliConfig.output,
      git: {
        ...cliConfig.output?.git,
        includeDiffs: true,
      },
    };
  }

  try {
    return repomixConfigCliSchema.parse(cliConfig);
  } catch (error) {
    rethrowValidationErrorIfZodError(error, 'Invalid cli arguments');
    throw error;
  }
};

```

## /src/cli/actions/initAction.ts

```ts path="/src/cli/actions/initAction.ts" 
import fs from 'node:fs/promises';
import path from 'node:path';
import * as prompts from '@clack/prompts';
import pc from 'picocolors';
import {
  type RepomixConfigFile,
  type RepomixOutputStyle,
  defaultConfig,
  defaultFilePathMap,
} from '../../config/configSchema.js';
import { getGlobalDirectory } from '../../config/globalDirectory.js';
import { getVersion } from '../../core/file/packageJsonParse.js';
import { logger } from '../../shared/logger.js';

const onCancelOperation = () => {
  prompts.cancel('Initialization cancelled.');
  process.exit(0);
};

export const runInitAction = async (rootDir: string, isGlobal: boolean): Promise<void> => {
  prompts.intro(pc.bold(`Welcome to Repomix ${isGlobal ? 'Global ' : ''}Configuration!`));

  try {
    // Step 1: Ask if user wants to create a config file
    const isCreatedConfig = await createConfigFile(rootDir, isGlobal);

    // Step 2: Ask if user wants to create a .repomixignore file
    const isCreatedIgnoreFile = await createIgnoreFile(rootDir, isGlobal);

    if (!isCreatedConfig && !isCreatedIgnoreFile) {
      prompts.outro(
        pc.yellow('No files were created. You can run this command again when you need to create configuration files.'),
      );
    } else {
      prompts.outro(pc.green('Initialization complete! You can now use Repomix with your specified settings.'));
    }
  } catch (error) {
    logger.error('An error occurred during initialization:', error);
  }
};

export const createConfigFile = async (rootDir: string, isGlobal: boolean): Promise<boolean> => {
  const configPath = path.resolve(isGlobal ? getGlobalDirectory() : rootDir, 'repomix.config.json');

  const isCreateConfig = await prompts.confirm({
    message: `Do you want to create a ${isGlobal ? 'global ' : ''}${pc.green('repomix.config.json')} file?`,
  });
  if (!isCreateConfig) {
    prompts.log.info(`Skipping ${pc.green('repomix.config.json')} file creation.`);
    return false;
  }
  if (prompts.isCancel(isCreateConfig)) {
    onCancelOperation();
    return false;
  }

  let isConfigFileExists = false;
  try {
    await fs.access(configPath);
    isConfigFileExists = true;
  } catch {
    // File doesn't exist, so we can proceed
  }

  if (isConfigFileExists) {
    const isOverwrite = await prompts.confirm({
      message: `A ${isGlobal ? 'global ' : ''}${pc.green('repomix.config.json')} file already exists. Do you want to overwrite it?`,
    });
    if (!isOverwrite) {
      prompts.log.info(`Skipping ${pc.green('repomix.config.json')} file creation.`);
      return false;
    }
    if (prompts.isCancel(isOverwrite)) {
      onCancelOperation();
      return false;
    }
  }

  const options = await prompts.group(
    {
      outputStyle: () => {
        return prompts.select({
          message: 'Output style:',
          options: [
            { value: 'xml', label: 'XML', hint: 'Structured XML format' },
            { value: 'markdown', label: 'Markdown', hint: 'Markdown format' },
            { value: 'plain', label: 'Plain', hint: 'Simple text format' },
          ],
          initialValue: defaultConfig.output.style,
        });
      },
      outputFilePath: ({ results }) => {
        const defaultFilePath = defaultFilePathMap[results.outputStyle as RepomixOutputStyle];
        return prompts.text({
          message: 'Output file path:',
          initialValue: defaultFilePath,
          validate: (value) => (value.length === 0 ? 'Output file path is required' : undefined),
        });
      },
    },
    {
      onCancel: onCancelOperation,
    },
  );

  const config: RepomixConfigFile = {
    $schema: 'https://repomix.com/schemas/latest/schema.json',
    ...defaultConfig,
    output: {
      ...defaultConfig.output,
      filePath: options.outputFilePath as string,
      style: options.outputStyle as RepomixOutputStyle,
    },
  };

  await fs.mkdir(path.dirname(configPath), { recursive: true });
  await fs.writeFile(configPath, JSON.stringify(config, null, 2));

  const relativeConfigPath = path.relative(rootDir, configPath);

  prompts.log.success(
    pc.green(`${isGlobal ? 'Global config' : 'Config'} file created!\n`) + pc.dim(`Path: ${relativeConfigPath}`),
  );

  return true;
};

export const createIgnoreFile = async (rootDir: string, isGlobal: boolean): Promise<boolean> => {
  if (isGlobal) {
    prompts.log.info(`Skipping ${pc.green('.repomixignore')} file creation for global configuration.`);
    return false;
  }

  const ignorePath = path.resolve(rootDir, '.repomixignore');
  const createIgnore = await prompts.confirm({
    message: `Do you want to create a ${pc.green('.repomixignore')} file?`,
  });
  if (!createIgnore) {
    prompts.log.info(`Skipping ${pc.green('.repomixignore')} file creation.`);
    return false;
  }
  if (prompts.isCancel(createIgnore)) {
    onCancelOperation();
    return false;
  }

  let isIgnoreFileExists = false;
  try {
    await fs.access(ignorePath);
    isIgnoreFileExists = true;
  } catch {
    // File doesn't exist, so we can proceed
  }

  if (isIgnoreFileExists) {
    const overwrite = await prompts.confirm({
      message: `A ${pc.green('.repomixignore')} file already exists. Do you want to overwrite it?`,
    });

    if (!overwrite) {
      prompts.log.info(`${pc.green('.repomixignore')} file creation skipped. Existing file will not be modified.`);
      return false;
    }
  }

  const defaultIgnoreContent = `# Add patterns to ignore here, one per line
# Example:
# *.log
# tmp/
`;

  await fs.writeFile(ignorePath, defaultIgnoreContent);
  prompts.log.success(
    pc.green('Created .repomixignore file!\n') + pc.dim(`Path: ${path.relative(rootDir, ignorePath)}`),
  );

  return true;
};

```

## /src/cli/actions/mcpAction.ts

```ts path="/src/cli/actions/mcpAction.ts" 
import { runMcpServer } from '../../mcp/mcpServer.js';
import { logger } from '../../shared/logger.js';

export const runMcpAction = async (): Promise<void> => {
  logger.trace('Starting Repomix MCP server...');
  await runMcpServer();
};

```

## /src/cli/actions/migrationAction.ts

```ts path="/src/cli/actions/migrationAction.ts" 
import * as fs from 'node:fs/promises';
import path from 'node:path';
import * as prompts from '@clack/prompts';
import pc from 'picocolors';
import { getGlobalDirectory } from '../../config/globalDirectory.js';
import { logger } from '../../shared/logger.js';

interface MigrationPaths {
  oldConfigPath: string;
  newConfigPath: string;
  oldIgnorePath: string;
  newIgnorePath: string;
  oldInstructionPath: string;
  newInstructionPath: string;
  oldOutputPaths: string[];
  newOutputPaths: string[];
  oldGlobalConfigPath: string;
  newGlobalConfigPath: string;
}

interface MigrationResult {
  configMigrated: boolean;
  ignoreMigrated: boolean;
  instructionMigrated: boolean;
  outputFilesMigrated: string[];
  globalConfigMigrated: boolean;
  error?: Error;
}

/**
 * Check if a file exists at the given path
 */
const fileExists = async (filePath: string): Promise<boolean> => {
  try {
    await fs.access(filePath);
    return true;
  } catch {
    return false;
  }
};

/**
 * Replace all occurrences of 'repopack' with 'repomix' in a string
 */
const replaceRepopackString = (content: string): string => {
  return content.replace(/repopack/g, 'repomix').replace(/Repopack/g, 'Repomix');
};

/**
 * Update file content by replacing 'repopack' with 'repomix'
 */
const updateFileContent = async (filePath: string): Promise<boolean> => {
  const content = await fs.readFile(filePath, 'utf8');
  const updatedContent = replaceRepopackString(content);

  // Check if content needs to be updated
  if (content !== updatedContent) {
    await fs.writeFile(filePath, updatedContent, 'utf8');
    const relativePath = path.relative(process.cwd(), filePath);
    logger.log(`Updated repopack references in ${pc.cyan(relativePath)}`);
    return true;
  }

  return false;
};

/**
 * Parse JSON content, update instructionFilePath if exists
 */
const updateInstructionPath = (content: string): string => {
  try {
    const config = JSON.parse(content);
    if (config.output?.instructionFilePath) {
      config.output.instructionFilePath = config.output.instructionFilePath.replace('repopack', 'repomix');
    }
    // Also update output.filePath if it exists
    if (config.output?.filePath) {
      config.output.filePath = config.output.filePath.replace('repopack', 'repomix');
    }
    return JSON.stringify(config, null, 2);
  } catch {
    return content;
  }
};

/**
 * Get output file paths pairs
 */
const getOutputFilePaths = (rootDir: string): { oldPaths: string[]; newPaths: string[] } => {
  const extensions = ['.txt', '.xml', '.md'];
  const oldPaths = extensions.map((ext) => path.join(rootDir, `repopack-output${ext}`));
  const newPaths = extensions.map((ext) => path.join(rootDir, `repomix-output${ext}`));
  return { oldPaths, newPaths };
};

/**
 * Migrate a single file from old path to new path
 */
const migrateFile = async (
  oldPath: string,
  newPath: string,
  description: string,
  isConfig = false,
): Promise<boolean> => {
  if (!(await fileExists(oldPath))) {
    return false;
  }

  const exists = await fileExists(newPath);
  if (exists) {
    const shouldOverwrite = await prompts.confirm({
      message: `${description} already exists at ${newPath}. Do you want to overwrite it?`,
    });

    if (prompts.isCancel(shouldOverwrite) || !shouldOverwrite) {
      logger.info(`Skipping migration of ${description}`);
      return false;
    }
  }

  try {
    // Read and update content
    let content = await fs.readFile(oldPath, 'utf8');
    content = replaceRepopackString(content);

    // For config files, also update instructionFilePath and output.filePath
    if (isConfig) {
      content = updateInstructionPath(content);
    }

    // Ensure the target directory exists
    await fs.mkdir(path.dirname(newPath), { recursive: true });

    // Write to new file
    await fs.writeFile(newPath, content, 'utf8');

    // Remove old file
    await fs.unlink(oldPath);

    const relativeOldPath = path.relative(process.cwd(), oldPath);
    const relativeNewPath = path.relative(process.cwd(), newPath);

    logger.log(`Renamed ${description} from ${relativeOldPath} to ${relativeNewPath}`);
    return true;
  } catch (error) {
    logger.error(`Failed to migrate ${description}:`, error);
    return false;
  }
};

/**
 * Update content of gitignore and repomixignore files
 */
const updateIgnoreFiles = async (rootDir: string): Promise<void> => {
  const gitignorePath = path.join(rootDir, '.gitignore');
  const repomixignorePath = path.join(rootDir, '.repomixignore');

  if (await fileExists(gitignorePath)) {
    const updated = await updateFileContent(gitignorePath);
    if (!updated) {
      logger.debug('No changes needed in .gitignore');
    }
  }

  if (await fileExists(repomixignorePath)) {
    const updated = await updateFileContent(repomixignorePath);
    if (!updated) {
      logger.debug('No changes needed in .repomixignore');
    }
  }
};

/**
 * Get all migration related file paths
 */
const getMigrationPaths = (rootDir: string): MigrationPaths => {
  const { oldPaths: oldOutputPaths, newPaths: newOutputPaths } = getOutputFilePaths(rootDir);
  const oldGlobalDirectory = path.join(process.env.HOME || '', '.config', 'repopack');
  const newGlobalDirectory = getGlobalDirectory();

  return {
    oldConfigPath: path.join(rootDir, 'repopack.config.json'),
    newConfigPath: path.join(rootDir, 'repomix.config.json'),
    oldIgnorePath: path.join(rootDir, '.repopackignore'),
    newIgnorePath: path.join(rootDir, '.repomixignore'),
    oldInstructionPath: path.join(rootDir, 'repopack-instruction.md'),
    newInstructionPath: path.join(rootDir, 'repomix-instruction.md'),
    oldOutputPaths,
    newOutputPaths,
    oldGlobalConfigPath: path.join(oldGlobalDirectory, 'repopack.config.json'),
    newGlobalConfigPath: path.join(newGlobalDirectory, 'repomix.config.json'),
  };
};

/**
 * Migrate output files
 */
const migrateOutputFiles = async (oldPaths: string[], newPaths: string[]): Promise<string[]> => {
  const migratedFiles: string[] = [];

  for (let i = 0; i < oldPaths.length; i++) {
    const oldPath = oldPaths[i];
    const newPath = newPaths[i];
    const ext = path.extname(oldPath);

    if (await migrateFile(oldPath, newPath, `Output file (${ext})`)) {
      migratedFiles.push(newPath);
    }
  }

  return migratedFiles;
};

export const runMigrationAction = async (rootDir: string): Promise<MigrationResult> => {
  const result: MigrationResult = {
    configMigrated: false,
    ignoreMigrated: false,
    instructionMigrated: false,
    outputFilesMigrated: [],
    globalConfigMigrated: false,
  };

  try {
    const paths = getMigrationPaths(rootDir);

    // Check if migration is needed
    const hasOldConfig = await fileExists(paths.oldConfigPath);
    const hasOldIgnore = await fileExists(paths.oldIgnorePath);
    const hasOldInstruction = await fileExists(paths.oldInstructionPath);
    const hasOldGlobalConfig = await fileExists(paths.oldGlobalConfigPath);
    const hasOldOutput = await Promise.all(paths.oldOutputPaths.map(fileExists)).then((results) =>
      results.some((exists) => exists),
    );

    if (!hasOldConfig && !hasOldIgnore && !hasOldInstruction && !hasOldOutput && !hasOldGlobalConfig) {
      logger.debug('No Repopack files found to migrate.');
      return result;
    }

    // Show migration notice based on what needs to be migrated
    let migrationMessage = `Found ${pc.green('Repopack')} `;
    const items = [];
    if (hasOldConfig || hasOldIgnore || hasOldInstruction || hasOldOutput) items.push('local configuration');
    if (hasOldGlobalConfig) items.push('global configuration');
    migrationMessage += `${items.join(' and ')}. Would you like to migrate to ${pc.green('Repomix')}?`;

    // Confirm migration with user
    const shouldMigrate = await prompts.confirm({
      message: migrationMessage,
    });

    if (prompts.isCancel(shouldMigrate) || !shouldMigrate) {
      logger.info('Migration cancelled.');
      return result;
    }

    // Show migration notice
    logger.info(pc.cyan('\nMigrating from Repopack to Repomix...'));
    logger.log('');

    // Migrate config file
    if (hasOldConfig) {
      result.configMigrated = await migrateFile(paths.oldConfigPath, paths.newConfigPath, 'Configuration file', true);
    }

    // Migrate global config file
    if (hasOldGlobalConfig) {
      result.globalConfigMigrated = await migrateFile(
        paths.oldGlobalConfigPath,
        paths.newGlobalConfigPath,
        'Global configuration file',
        true,
      );
    }

    // Migrate ignore file
    if (hasOldIgnore) {
      result.ignoreMigrated = await migrateFile(paths.oldIgnorePath, paths.newIgnorePath, 'Ignore file');
    }

    // Migrate instruction file
    if (hasOldInstruction) {
      result.instructionMigrated = await migrateFile(
        paths.oldInstructionPath,
        paths.newInstructionPath,
        'Instruction file',
      );
    }

    // Migrate output files
    if (hasOldOutput) {
      result.outputFilesMigrated = await migrateOutputFiles(paths.oldOutputPaths, paths.newOutputPaths);
    }

    // Update content in gitignore and repomixignore
    await updateIgnoreFiles(rootDir);

    // Show success message
    if (
      result.configMigrated ||
      result.ignoreMigrated ||
      result.instructionMigrated ||
      result.outputFilesMigrated.length > 0 ||
      result.globalConfigMigrated
    ) {
      logger.log('');
      logger.success('✔ Migration completed successfully!');
      logger.log('');
      logger.info(
        'You can now use Repomix commands as usual. The old Repopack files have been migrated to the new format.',
      );
      logger.log('');
    }

    return result;
  } catch (error) {
    if (error instanceof Error) {
      result.error = error;
    } else {
      result.error = new Error(String(error));
    }
    logger.error('An error occurred during migration:', error);
    return result;
  }
};

```

## /src/cli/actions/remoteAction.ts

```ts path="/src/cli/actions/remoteAction.ts" 
import * as fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import pc from 'picocolors';
import { execGitShallowClone } from '../../core/git/gitCommand.js';
import { getRemoteRefs } from '../../core/git/gitRemoteHandle.js';
import { parseRemoteValue } from '../../core/git/gitRemoteParse.js';
import { isGitInstalled } from '../../core/git/gitRepositoryHandle.js';
import { RepomixError } from '../../shared/errorHandle.js';
import { logger } from '../../shared/logger.js';
import { Spinner } from '../cliSpinner.js';
import type { CliOptions } from '../types.js';
import { type DefaultActionRunnerResult, runDefaultAction } from './defaultAction.js';

export const runRemoteAction = async (
  repoUrl: string,
  cliOptions: CliOptions,
  deps = {
    isGitInstalled,
    execGitShallowClone,
    getRemoteRefs,
    runDefaultAction,
  },
): Promise<DefaultActionRunnerResult> => {
  if (!(await deps.isGitInstalled())) {
    throw new RepomixError('Git is not installed or not in the system PATH.');
  }

  // Get remote refs
  let refs: string[] = [];
  try {
    refs = await deps.getRemoteRefs(parseRemoteValue(repoUrl).repoUrl);
    logger.trace(`Retrieved ${refs.length} refs from remote repository`);
  } catch (error) {
    logger.trace('Failed to get remote refs, proceeding without them:', (error as Error).message);
  }

  // Parse the remote URL with the refs information
  const parsedFields = parseRemoteValue(repoUrl, refs);

  const spinner = new Spinner('Cloning repository...', cliOptions);
  const tempDirPath = await createTempDirectory();
  let result: DefaultActionRunnerResult;

  try {
    spinner.start();

    // Clone the repository
    await cloneRepository(parsedFields.repoUrl, tempDirPath, cliOptions.remoteBranch || parsedFields.remoteBranch, {
      execGitShallowClone: deps.execGitShallowClone,
    });

    spinner.succeed('Repository cloned successfully!');
    logger.log('');

    // Run the default action on the cloned repository
    result = await deps.runDefaultAction([tempDirPath], tempDirPath, cliOptions);
    await copyOutputToCurrentDirectory(tempDirPath, process.cwd(), result.config.output.filePath);
  } catch (error) {
    spinner.fail('Error during repository cloning. cleanup...');
    throw error;
  } finally {
    // Cleanup the temporary directory
    await cleanupTempDirectory(tempDirPath);
  }

  return result;
};

export const createTempDirectory = async (): Promise<string> => {
  const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'repomix-'));
  logger.trace(`Created temporary directory. (path: ${pc.dim(tempDir)})`);
  return tempDir;
};

export const cloneRepository = async (
  url: string,
  directory: string,
  remoteBranch?: string,
  deps = {
    execGitShallowClone,
  },
): Promise<void> => {
  logger.log(`Clone repository: ${url} to temporary directory. ${pc.dim(`path: ${directory}`)}`);
  logger.log('');

  try {
    await deps.execGitShallowClone(url, directory, remoteBranch);
  } catch (error) {
    throw new RepomixError(`Failed to clone repository: ${(error as Error).message}`);
  }
};

export const cleanupTempDirectory = async (directory: string): Promise<void> => {
  logger.trace(`Cleaning up temporary directory: ${directory}`);
  await fs.rm(directory, { recursive: true, force: true });
};

export const copyOutputToCurrentDirectory = async (
  sourceDir: string,
  targetDir: string,
  outputFileName: string,
): Promise<void> => {
  const sourcePath = path.resolve(sourceDir, outputFileName);
  const targetPath = path.resolve(targetDir, outputFileName);

  try {
    logger.trace(`Copying output file from: ${sourcePath} to: ${targetPath}`);

    // Create target directory if it doesn't exist
    await fs.mkdir(path.dirname(targetPath), { recursive: true });

    await fs.copyFile(sourcePath, targetPath);
  } catch (error) {
    throw new RepomixError(`Failed to copy output file: ${(error as Error).message}`);
  }
};

```

## /src/cli/actions/versionAction.ts

```ts path="/src/cli/actions/versionAction.ts" 
import { getVersion } from '../../core/file/packageJsonParse.js';
import { logger } from '../../shared/logger.js';

export const runVersionAction = async (): Promise<void> => {
  const version = await getVersion();
  logger.log(version);
};

```

## /src/cli/cliPrint.ts

```ts path="/src/cli/cliPrint.ts" 
import path from 'node:path';
import pc from 'picocolors';
import type { RepomixConfigMerged } from '../config/configSchema.js';
import type { PackResult } from '../core/packager.js';
import type { SuspiciousFileResult } from '../core/security/securityCheck.js';
import { logger } from '../shared/logger.js';

export const printSummary = (packResult: PackResult, config: RepomixConfigMerged) => {
  let securityCheckMessage = '';
  if (config.security.enableSecurityCheck) {
    if (packResult.suspiciousFilesResults.length > 0) {
      securityCheckMessage = pc.yellow(
        `${packResult.suspiciousFilesResults.length.toLocaleString()} suspicious file(s) detected and excluded`,
      );
    } else {
      securityCheckMessage = pc.white('✔ No suspicious files detected');
    }
  } else {
    securityCheckMessage = pc.dim('Security check disabled');
  }

  logger.log(pc.white('📊 Pack Summary:'));
  logger.log(pc.dim('────────────────'));
  logger.log(`${pc.white('  Total Files:')} ${pc.white(packResult.totalFiles.toLocaleString())} files`);
  logger.log(`${pc.white('  Total Chars:')} ${pc.white(packResult.totalCharacters.toLocaleString())} chars`);
  logger.log(`${pc.white(' Total Tokens:')} ${pc.white(packResult.totalTokens.toLocaleString())} tokens`);
  logger.log(`${pc.white('       Output:')} ${pc.white(config.output.filePath)}`);
  logger.log(`${pc.white('     Security:')} ${pc.white(securityCheckMessage)}`);

  if (config.output.git?.includeDiffs) {
    let gitDiffsMessage = '';
    if (packResult.gitDiffTokenCount) {
      gitDiffsMessage = pc.white(
        `✔ Git diffs included ${pc.dim(`(${packResult.gitDiffTokenCount.toLocaleString()} tokens)`)}`,
      );
    } else {
      gitDiffsMessage = pc.dim('✖ No git diffs included');
    }
    logger.log(`${pc.white('    Git Diffs:')} ${gitDiffsMessage}`);
  }
};

export const printSecurityCheck = (
  rootDir: string,
  suspiciousFilesResults: SuspiciousFileResult[],
  suspiciousGitDiffResults: SuspiciousFileResult[],
  config: RepomixConfigMerged,
) => {
  if (!config.security.enableSecurityCheck) {
    return;
  }

  logger.log(pc.white('🔎 Security Check:'));
  logger.log(pc.dim('──────────────────'));

  // Print results for files
  if (suspiciousFilesResults.length === 0) {
    logger.log(`${pc.green('✔')} ${pc.white('No suspicious files detected.')}`);
  } else {
    logger.log(pc.yellow(`${suspiciousFilesResults.length} suspicious file(s) detected and excluded from the output:`));
    suspiciousFilesResults.forEach((suspiciousFilesResult, index) => {
      const relativeFilePath = path.relative(rootDir, suspiciousFilesResult.filePath);
      logger.log(`${pc.white(`${index + 1}.`)} ${pc.white(relativeFilePath)}`);
      logger.log(pc.dim(`   - ${suspiciousFilesResult.messages.join('\n   - ')}`));
    });
    logger.log(pc.yellow('\nThese files have been excluded from the output for security reasons.'));
    logger.log(pc.yellow('Please review these files for potential sensitive information.'));
  }

  // Print results for git diffs
  if (suspiciousGitDiffResults.length > 0) {
    logger.log('');
    logger.log(pc.yellow(`${suspiciousGitDiffResults.length} security issue(s) found in Git diffs:`));
    suspiciousGitDiffResults.forEach((suspiciousResult, index) => {
      logger.log(`${pc.white(`${index + 1}.`)} ${pc.white(suspiciousResult.filePath)}`);
      logger.log(pc.dim(`   - ${suspiciousResult.messages.join('\n   - ')}`));
    });
    logger.log(pc.yellow('\nNote: Git diffs with security issues are still included in the output.'));
    logger.log(pc.yellow('Please review the diffs before sharing the output.'));
  }
};

export const printTopFiles = (
  fileCharCounts: Record<string, number>,
  fileTokenCounts: Record<string, number>,
  topFilesLength: number,
) => {
  const topFilesLengthStrLen = topFilesLength.toString().length;
  logger.log(pc.white(`📈 Top ${topFilesLength} Files by Character Count and Token Count:`));
  logger.log(pc.dim(`─────────────────────────────────────────────────${'─'.repeat(topFilesLengthStrLen)}`));

  const topFiles = Object.entries(fileCharCounts)
    .sort((a, b) => b[1] - a[1])
    .slice(0, topFilesLength);

  // Calculate total token count
  const totalTokens = Object.values(fileTokenCounts).reduce((sum, count) => sum + count, 0);

  topFiles.forEach(([filePath, charCount], index) => {
    const tokenCount = fileTokenCounts[filePath];
    const percentageOfTotal = totalTokens > 0 ? Number(((tokenCount / totalTokens) * 100).toFixed(1)) : 0;
    const indexString = `${index + 1}.`.padEnd(3, ' ');
    logger.log(
      `${pc.white(`${indexString}`)} ${pc.white(filePath)} ${pc.dim(`(${charCount.toLocaleString()} chars, ${tokenCount.toLocaleString()} tokens, ${percentageOfTotal}%)`)}`,
    );
  });
};

export const printCompletion = () => {
  logger.log(pc.green('🎉 All Done!'));
  logger.log(pc.white('Your repository has been successfully packed.'));

  logger.log('');
  logger.log(`💡 Repomix is now available in your browser! Try it at ${pc.underline('https://repomix.com')}`);
};

```

## /src/cli/cliRun.ts

```ts path="/src/cli/cliRun.ts" 
import process from 'node:process';
import { Command, Option, program } from 'commander';
import pc from 'picocolors';
import { getVersion } from '../core/file/packageJsonParse.js';
import { handleError } from '../shared/errorHandle.js';
import { logger, repomixLogLevels } from '../shared/logger.js';
import { runDefaultAction } from './actions/defaultAction.js';
import { runInitAction } from './actions/initAction.js';
import { runMcpAction } from './actions/mcpAction.js';
import { runRemoteAction } from './actions/remoteAction.js';
import { runVersionAction } from './actions/versionAction.js';
import type { CliOptions } from './types.js';

// Semantic mapping for CLI suggestions
// This maps conceptually related terms (not typos) to valid options
const semanticSuggestionMap: Record<string, string[]> = {
  exclude: ['--ignore'],
  reject: ['--ignore'],
  omit: ['--ignore'],
  skip: ['--ignore'],
  blacklist: ['--ignore'],
  save: ['--output'],
  export: ['--output'],
  out: ['--output'],
  file: ['--output'],
  format: ['--style'],
  type: ['--style'],
  syntax: ['--style'],
  debug: ['--verbose'],
  detailed: ['--verbose'],
  silent: ['--quiet'],
  mute: ['--quiet'],
  add: ['--include'],
  with: ['--include'],
  whitelist: ['--include'],
  clone: ['--remote'],
  git: ['--remote'],
  minimize: ['--compress'],
  reduce: ['--compress'],
  'strip-comments': ['--remove-comments'],
  'no-comments': ['--remove-comments'],
  print: ['--stdout'],
  console: ['--stdout'],
  terminal: ['--stdout'],
};

export const run = async () => {
  try {
    program
      .description('Repomix - Pack your repository into a single AI-friendly file')
      .argument('[directories...]', 'list of directories to process', ['.'])
      // Basic Options
      .optionsGroup('Basic Options')
      .option('-v, --version', 'show version information')
      // Output Options
      .optionsGroup('Output Options')
      .option('-o, --output <file>', 'specify the output file name')
      .addOption(new Option('--stdout', 'output to stdout instead of writing to a file').conflicts('output'))
      .option('--style <type>', 'specify the output style (xml, markdown, plain)')
      .option('--parsable-style', 'by escaping and formatting, ensure the output is parsable as a document of its type')
      .option('--compress', 'perform code compression to reduce token count')
      .option('--output-show-line-numbers', 'add line numbers to each line in the output')
      .option('--copy', 'copy generated output to system clipboard')
      .option('--no-file-summary', 'disable file summary section output')
      .option('--no-directory-structure', 'disable directory structure section output')
      .option('--no-files', 'disable files content output (metadata-only mode)')
      .option('--remove-comments', 'remove comments')
      .option('--remove-empty-lines', 'remove empty lines')
      .option('--header-text <text>', 'specify the header text')
      .option('--instruction-file-path <path>', 'path to a file containing detailed custom instructions')
      .option('--include-empty-directories', 'include empty directories in the output')
      .option('--no-git-sort-by-changes', 'disable sorting files by git change count')
      .option(
        '--include-diffs',
        'include git diffs in the output (includes both work tree and staged changes separately)',
      )
      // Filter Options
      .optionsGroup('Filter Options')
      .option('--include <patterns>', 'list of include patterns (comma-separated)')
      .option('-i, --ignore <patterns>', 'additional ignore patterns (comma-separated)')
      .option('--no-gitignore', 'disable .gitignore file usage')
      .option('--no-default-patterns', 'disable default patterns')
      // Remote Repository Options
      .optionsGroup('Remote Repository Options')
      .option('--remote <url>', 'process a remote Git repository')
      .option(
        '--remote-branch <name>',
        'specify the remote branch name, tag, or commit hash (defaults to repository default branch)',
      )
      // Configuration Options
      .optionsGroup('Configuration Options')
      .option('-c, --config <path>', 'path to a custom config file')
      .option('--init', 'initialize a new repomix.config.json file')
      .option('--global', 'use global configuration (only applicable with --init)')
      // Security Options
      .optionsGroup('Security Options')
      .option('--no-security-check', 'disable security check')
      // Token Count Options
      .optionsGroup('Token Count Options')
      .option('--token-count-encoding <encoding>', 'specify token count encoding (e.g., o200k_base, cl100k_base)')
      // MCP
      .optionsGroup('MCP')
      .option('--mcp', 'run as a MCP server')
      // Other Options
      .optionsGroup('Other Options')
      .option('--top-files-len <number>', 'specify the number of top files to display', Number.parseInt)
      .addOption(new Option('--verbose', 'enable verbose logging for detailed output').conflicts('quiet'))
      .addOption(new Option('--quiet', 'disable all output to stdout').conflicts('verbose'))
      .action(commanderActionEndpoint);

    // Custom error handling function
    const configOutput = program.configureOutput();
    const originalOutputError = configOutput.outputError || ((str, write) => write(str));

    program.configureOutput({
      outputError: (str, write) => {
        // Check if this is an unknown option error
        if (str.includes('unknown option')) {
          const match = str.match(/unknown option '?(-{1,2}[^ ']+)'?/i);
          if (match?.[1]) {
            const unknownOption = match[1];
            const cleanOption = unknownOption.replace(/^-+/, '');

            // Check if the option has a semantic match
            const semanticMatches = semanticSuggestionMap[cleanOption];
            if (semanticMatches) {
              // We have a direct semantic match
              logger.error(`✖ Unknown option: ${unknownOption}`);
              logger.info(`Did you mean: ${semanticMatches.join(' or ')}?`);
              return;
            }
          }
        }

        // Fall back to the original Commander error handler
        originalOutputError(str, write);
      },
    });

    await program.parseAsync(process.argv);
  } catch (error) {
    handleError(error);
  }
};

const commanderActionEndpoint = async (directories: string[], options: CliOptions = {}) => {
  await runCli(directories, process.cwd(), options);
};

export const runCli = async (directories: string[], cwd: string, options: CliOptions) => {
  // Detect stdout mode
  // NOTE: For compatibility, currently not detecting pipe mode
  const isForceStdoutMode = options.output === '-';
  if (isForceStdoutMode) {
    options.stdout = true;
  }

  // Set log level based on verbose and quiet flags
  if (options.quiet) {
    logger.setLogLevel(repomixLogLevels.SILENT);
  } else if (options.verbose) {
    logger.setLogLevel(repomixLogLevels.DEBUG);
  } else {
    logger.setLogLevel(repomixLogLevels.INFO);
  }

  // In stdout mode, set log level to SILENT
  if (options.stdout) {
    logger.setLogLevel(repomixLogLevels.SILENT);
  }

  logger.trace('directories:', directories);
  logger.trace('cwd:', cwd);
  logger.trace('options:', options);

  if (options.mcp) {
    return await runMcpAction();
  }

  if (options.version) {
    await runVersionAction();
    return;
  }

  const version = await getVersion();
  logger.log(pc.dim(`\n📦 Repomix v${version}\n`));

  if (options.init) {
    await runInitAction(cwd, options.global || false);
    return;
  }

  if (options.remote) {
    return await runRemoteAction(options.remote, options);
  }

  return await runDefaultAction(directories, cwd, options);
};

```

## /src/cli/cliSpinner.ts

```ts path="/src/cli/cliSpinner.ts" 
import cliSpinners from 'cli-spinners';
import logUpdate from 'log-update';
import pc from 'picocolors';
import type { CliOptions } from './types.js';

export class Spinner {
  private spinner = cliSpinners.dots;
  private message: string;
  private currentFrame = 0;
  private interval: ReturnType<typeof setInterval> | null = null;
  private readonly isQuiet: boolean;

  constructor(message: string, cliOptions: CliOptions) {
    this.message = message;
    // If the user has specified the verbose flag, don't show the spinner
    this.isQuiet = cliOptions.quiet || cliOptions.verbose || cliOptions.stdout || false;
  }

  start(): void {
    if (this.isQuiet) {
      return;
    }

    const frames = this.spinner.frames;
    const framesLength = frames.length;
    this.interval = setInterval(() => {
      this.currentFrame++;
      const frame = frames[this.currentFrame % framesLength];
      logUpdate(`${pc.cyan(frame)} ${this.message}`);
    }, this.spinner.interval);
  }

  update(message: string): void {
    if (this.isQuiet) {
      return;
    }

    this.message = message;
  }

  stop(finalMessage: string): void {
    if (this.isQuiet) {
      return;
    }

    if (this.interval) {
      clearInterval(this.interval);
      this.interval = null;
    }
    logUpdate(finalMessage);
    logUpdate.done();
  }

  succeed(message: string): void {
    if (this.isQuiet) {
      return;
    }

    this.stop(`${pc.green('✔')} ${message}`);
  }

  fail(message: string): void {
    if (this.isQuiet) {
      return;
    }

    this.stop(`${pc.red('✖')} ${message}`);
  }
}

```

## /src/cli/types.ts

```ts path="/src/cli/types.ts" 
import type { OptionValues } from 'commander';
import type { RepomixOutputStyle } from '../config/configSchema.js';

export interface CliOptions extends OptionValues {
  // Basic Options
  version?: boolean;

  // Output Options
  output?: string;
  stdout?: boolean;
  style?: RepomixOutputStyle;
  parsableStyle?: boolean;
  compress?: boolean;
  outputShowLineNumbers?: boolean;
  copy?: boolean;
  fileSummary?: boolean;
  directoryStructure?: boolean;
  files?: boolean;
  removeComments?: boolean;
  removeEmptyLines?: boolean;
  headerText?: string;
  instructionFilePath?: string;
  includeEmptyDirectories?: boolean;
  gitSortByChanges?: boolean;
  includeDiffs?: boolean;

  // Filter Options
  include?: string;
  ignore?: string;
  gitignore?: boolean;
  defaultPatterns?: boolean;

  // Remote Repository Options
  remote?: string;
  remoteBranch?: string;

  // Configuration Options
  config?: string;
  init?: boolean;
  global?: boolean;

  // Security Options
  securityCheck?: boolean;

  // Token Count Options
  tokenCountEncoding?: string;

  // MCP
  mcp?: boolean;

  // Other Options
  topFilesLen?: number;
  verbose?: boolean;
  quiet?: boolean;
}

```

## /src/config/configLoad.ts

```ts path="/src/config/configLoad.ts" 
import * as fs from 'node:fs/promises';
import path from 'node:path';
import JSON5 from 'json5';
import pc from 'picocolors';
import { RepomixError, rethrowValidationErrorIfZodError } from '../shared/errorHandle.js';
import { logger } from '../shared/logger.js';
import {
  type RepomixConfigCli,
  type RepomixConfigFile,
  type RepomixConfigMerged,
  defaultConfig,
  defaultFilePathMap,
  repomixConfigFileSchema,
  repomixConfigMergedSchema,
} from './configSchema.js';
import { getGlobalDirectory } from './globalDirectory.js';

const defaultConfigPath = 'repomix.config.json';

const getGlobalConfigPath = () => {
  return path.join(getGlobalDirectory(), 'repomix.config.json');
};

export const loadFileConfig = async (rootDir: string, argConfigPath: string | null): Promise<RepomixConfigFile> => {
  let useDefaultConfig = false;
  let configPath = argConfigPath;
  if (!configPath) {
    useDefaultConfig = true;
    configPath = defaultConfigPath;
  }

  const fullPath = path.resolve(rootDir, configPath);

  logger.trace('Loading local config from:', fullPath);

  // Check local file existence
  const isLocalFileExists = await fs
    .stat(fullPath)
    .then((stats) => stats.isFile())
    .catch(() => false);

  if (isLocalFileExists) {
    return await loadAndValidateConfig(fullPath);
  }

  if (useDefaultConfig) {
    // Try to load global config
    const globalConfigPath = getGlobalConfigPath();
    logger.trace('Loading global config from:', globalConfigPath);

    const isGlobalFileExists = await fs
      .stat(globalConfigPath)
      .then((stats) => stats.isFile())
      .catch(() => false);

    if (isGlobalFileExists) {
      return await loadAndValidateConfig(globalConfigPath);
    }

    logger.log(
      pc.dim(
        `No custom config found at ${configPath} or global config at ${globalConfigPath}.\nYou can add a config file for additional settings. Please check https://github.com/yamadashy/repomix for more information.`,
      ),
    );
    return {};
  }
  throw new RepomixError(`Config file not found at ${configPath}`);
};

const loadAndValidateConfig = async (filePath: string): Promise<RepomixConfigFile> => {
  try {
    const fileContent = await fs.readFile(filePath, 'utf-8');
    const config = JSON5.parse(fileContent);
    return repomixConfigFileSchema.parse(config);
  } catch (error) {
    rethrowValidationErrorIfZodError(error, 'Invalid config schema');
    if (error instanceof SyntaxError) {
      throw new RepomixError(`Invalid JSON5 in config file ${filePath}: ${error.message}`);
    }
    if (error instanceof Error) {
      throw new RepomixError(`Error loading config from ${filePath}: ${error.message}`);
    }
    throw new RepomixError(`Error loading config from ${filePath}`);
  }
};

export const mergeConfigs = (
  cwd: string,
  fileConfig: RepomixConfigFile,
  cliConfig: RepomixConfigCli,
): RepomixConfigMerged => {
  logger.trace('Default config:', defaultConfig);

  const baseConfig = defaultConfig;

  // If the output file path is not provided in the config file or CLI, use the default file path for the style
  if (cliConfig.output?.filePath == null && fileConfig.output?.filePath == null) {
    const style = cliConfig.output?.style || fileConfig.output?.style || baseConfig.output.style;
    baseConfig.output.filePath = defaultFilePathMap[style];

    logger.trace('Default output file path is set to:', baseConfig.output.filePath);
  }

  const mergedConfig = {
    cwd,
    input: {
      ...baseConfig.input,
      ...fileConfig.input,
      ...cliConfig.input,
    },
    output: {
      ...baseConfig.output,
      ...fileConfig.output,
      ...cliConfig.output,
    },
    include: [...(baseConfig.include || []), ...(fileConfig.include || []), ...(cliConfig.include || [])],
    ignore: {
      ...baseConfig.ignore,
      ...fileConfig.ignore,
      ...cliConfig.ignore,
      customPatterns: [
        ...(baseConfig.ignore.customPatterns || []),
        ...(fileConfig.ignore?.customPatterns || []),
        ...(cliConfig.ignore?.customPatterns || []),
      ],
    },
    security: {
      ...baseConfig.security,
      ...fileConfig.security,
      ...cliConfig.security,
    },
  };

  try {
    return repomixConfigMergedSchema.parse(mergedConfig);
  } catch (error) {
    rethrowValidationErrorIfZodError(error, 'Invalid merged config');
    throw error;
  }
};

```

## /src/config/configSchema.ts

```ts path="/src/config/configSchema.ts" 
import type { TiktokenEncoding } from 'tiktoken';
import { z } from 'zod';

// Output style enum
export const repomixOutputStyleSchema = z.enum(['xml', 'markdown', 'plain']);
export type RepomixOutputStyle = z.infer<typeof repomixOutputStyleSchema>;

// Default values map
export const defaultFilePathMap: Record<RepomixOutputStyle, string> = {
  xml: 'repomix-output.xml',
  markdown: 'repomix-output.md',
  plain: 'repomix-output.txt',
} as const;

// Base config schema
export const repomixConfigBaseSchema = z.object({
  $schema: z.string().optional(),
  input: z
    .object({
      maxFileSize: z.number().optional(),
    })
    .optional(),
  output: z
    .object({
      filePath: z.string().optional(),
      style: repomixOutputStyleSchema.optional(),
      parsableStyle: z.boolean().optional(),
      headerText: z.string().optional(),
      instructionFilePath: z.string().optional(),
      fileSummary: z.boolean().optional(),
      directoryStructure: z.boolean().optional(),
      files: z.boolean().optional(),
      removeComments: z.boolean().optional(),
      removeEmptyLines: z.boolean().optional(),
      compress: z.boolean().optional(),
      topFilesLength: z.number().optional(),
      showLineNumbers: z.boolean().optional(),
      copyToClipboard: z.boolean().optional(),
      includeEmptyDirectories: z.boolean().optional(),
      git: z
        .object({
          sortByChanges: z.boolean().optional(),
          sortByChangesMaxCommits: z.number().optional(),
          includeDiffs: z.boolean().optional(),
        })
        .optional(),
    })
    .optional(),
  include: z.array(z.string()).optional(),
  ignore: z
    .object({
      useGitignore: z.boolean().optional(),
      useDefaultPatterns: z.boolean().optional(),
      customPatterns: z.array(z.string()).optional(),
    })
    .optional(),
  security: z
    .object({
      enableSecurityCheck: z.boolean().optional(),
    })
    .optional(),
  tokenCount: z
    .object({
      encoding: z.string().optional(),
    })
    .optional(),
});

// Default config schema with default values
export const repomixConfigDefaultSchema = z.object({
  input: z
    .object({
      maxFileSize: z
        .number()
        .int()
        .min(1)
        .default(50 * 1024 * 1024), // Default: 50MB
    })
    .default({}),
  output: z
    .object({
      filePath: z.string().default(defaultFilePathMap.xml),
      style: repomixOutputStyleSchema.default('xml'),
      parsableStyle: z.boolean().default(false),
      headerText: z.string().optional(),
      instructionFilePath: z.string().optional(),
      fileSummary: z.boolean().default(true),
      directoryStructure: z.boolean().default(true),
      files: z.boolean().default(true),
      removeComments: z.boolean().default(false),
      removeEmptyLines: z.boolean().default(false),
      compress: z.boolean().default(false),
      topFilesLength: z.number().int().min(0).default(5),
      showLineNumbers: z.boolean().default(false),
      copyToClipboard: z.boolean().default(false),
      includeEmptyDirectories: z.boolean().optional(),
      git: z
        .object({
          sortByChanges: z.boolean().default(true),
          sortByChangesMaxCommits: z.number().int().min(1).default(100),
          includeDiffs: z.boolean().default(false),
        })
        .default({}),
    })
    .default({}),
  include: z.array(z.string()).default([]),
  ignore: z
    .object({
      useGitignore: z.boolean().default(true),
      useDefaultPatterns: z.boolean().default(true),
      customPatterns: z.array(z.string()).default([]),
    })
    .default({}),
  security: z
    .object({
      enableSecurityCheck: z.boolean().default(true),
    })
    .default({}),
  tokenCount: z
    .object({
      encoding: z
        .string()
        .default('o200k_base')
        .transform((val) => val as TiktokenEncoding),
    })
    .default({}),
});

// File-specific schema. Add options for file path and style
export const repomixConfigFileSchema = repomixConfigBaseSchema;

// CLI-specific schema. Add options for standard output mode
export const repomixConfigCliSchema = repomixConfigBaseSchema.and(
  z.object({
    output: z
      .object({
        stdout: z.boolean().optional(),
      })
      .optional(),
  }),
);

// Merged schema for all configurations
export const repomixConfigMergedSchema = repomixConfigDefaultSchema
  .and(repomixConfigFileSchema)
  .and(repomixConfigCliSchema)
  .and(
    z.object({
      cwd: z.string(),
    }),
  );

export type RepomixConfigDefault = z.infer<typeof repomixConfigDefaultSchema>;
export type RepomixConfigFile = z.infer<typeof repomixConfigFileSchema>;
export type RepomixConfigCli = z.infer<typeof repomixConfigCliSchema>;
export type RepomixConfigMerged = z.infer<typeof repomixConfigMergedSchema>;

export const defaultConfig = repomixConfigDefaultSchema.parse({});

```

## /src/config/defaultIgnore.ts

```ts path="/src/config/defaultIgnore.ts" 
export const defaultIgnoreList = [
  // Version control
  '.git/**',
  '.hg/**',
  '.hgignore',
  '.svn/**',

  // Dependency directories
  '**/node_modules/**',
  '**/bower_components/**',
  '**/jspm_packages/**',
  'vendor/**',
  '**/.bundle/**',
  '**/.gradle/**',
  'target/**',

  // Logs
  'logs/**',
  '**/*.log',
  '**/npm-debug.log*',
  '**/yarn-debug.log*',
  '**/yarn-error.log*',

  // Runtime data
  'pids/**',
  '*.pid',
  '*.seed',
  '*.pid.lock',

  // Directory for instrumented libs generated by jscoverage/JSCover
  'lib-cov/**',

  // Coverage directory used by tools like istanbul
  'coverage/**',

  // nyc test coverage
  '.nyc_output/**',

  // Grunt intermediate storage
  '.grunt/**',

  // node-waf configuration
  '.lock-wscript',

  // Compiled binary addons
  'build/Release/**',

  // TypeScript v1 declaration files
  'typings/**',

  // Optional npm cache directory
  '**/.npm/**',

  // Cache directories
  '.eslintcache',
  '.rollup.cache/**',
  '.webpack.cache/**',
  '.parcel-cache/**',
  '.sass-cache/**',
  '*.cache',

  // Optional REPL history
  '.node_repl_history',

  // Output of 'npm pack'
  '*.tgz',

  // Yarn files
  '**/.yarn/**',

  // Yarn Integrity file
  '**/.yarn-integrity',

  // dotenv environment variables file
  '.env',

  // next.js build output
  '.next/**',

  // nuxt.js build output
  '.nuxt/**',

  // vuepress build output
  '.vuepress/dist/**',

  // Serverless directories
  '.serverless/**',

  // FuseBox cache
  '.fusebox/**',

  // DynamoDB Local files
  '.dynamodb/**',

  // TypeScript output
  'dist/**',

  // OS generated files
  '**/.DS_Store',
  '**/Thumbs.db',

  // Editor directories and files
  '.idea/**',
  '.vscode/**',
  '**/*.swp',
  '**/*.swo',
  '**/*.swn',
  '**/*.bak',

  // Build outputs
  'build/**',
  'out/**',

  // Temporary files
  'tmp/**',
  'temp/**',

  // repomix output
  '**/repomix-output.*',
  '**/repopack-output.*', // Legacy

  // Essential Node.js-related entries
  '**/package-lock.json',
  '**/yarn-error.log',
  '**/yarn.lock',
  '**/pnpm-lock.yaml',
  '**/bun.lockb',
  '**/bun.lock',

  // Essential Python-related entries
  '**/__pycache__/**',
  '**/*.py[cod]',
  '**/venv/**',
  '**/.venv/**',
  '**/.pytest_cache/**',
  '**/.mypy_cache/**',
  '**/.ipynb_checkpoints/**',
  '**/Pipfile.lock',
  '**/poetry.lock',
  '**/uv.lock',

  // Essential Rust-related entries
  '**/Cargo.lock',
  '**/Cargo.toml.orig',
  '**/target/**',
  '**/*.rs.bk',

  // Essential PHP-related entries
  '**/composer.lock',

  // Essential Ruby-related entries
  '**/Gemfile.lock',

  // Essential Go-related entries
  '**/go.sum',

  // Essential Elixir-related entries
  '**/mix.lock',

  // Essential Haskell-related entries
  '**/stack.yaml.lock',
  '**/cabal.project.freeze',
];

```

## /src/config/globalDirectory.ts

```ts path="/src/config/globalDirectory.ts" 
import os from 'node:os';
import path from 'node:path';

export const getGlobalDirectory = () => {
  if (process.platform === 'win32') {
    const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
    return path.join(localAppData, 'Repomix');
  }

  if (process.env.XDG_CONFIG_HOME) {
    return path.join(process.env.XDG_CONFIG_HOME, 'repomix');
  }

  return path.join(os.homedir(), '.config', 'repomix');
};

```

## /src/core/file/fileCollect.ts

```ts path="/src/core/file/fileCollect.ts" 
import pc from 'picocolors';
import type { RepomixConfigMerged } from '../../config/configSchema.js';
import { logger } from '../../shared/logger.js';
import { initPiscina } from '../../shared/processConcurrency.js';
import type { RepomixProgressCallback } from '../../shared/types.js';
import type { RawFile } from './fileTypes.js';
import type { FileCollectTask } from './workers/fileCollectWorker.js';

const initTaskRunner = (numOfTasks: number) => {
  const pool = initPiscina(numOfTasks, new URL('./workers/fileCollectWorker.js', import.meta.url).href);
  return (task: FileCollectTask) => pool.run(task);
};

export const collectFiles = async (
  filePaths: string[],
  rootDir: string,
  config: RepomixConfigMerged,
  progressCallback: RepomixProgressCallback = () => {},
  deps = {
    initTaskRunner,
  },
): Promise<RawFile[]> => {
  const runTask = deps.initTaskRunner(filePaths.length);
  const tasks = filePaths.map(
    (filePath) =>
      ({
        filePath,
        rootDir,
        maxFileSize: config.input.maxFileSize,
      }) satisfies FileCollectTask,
  );

  try {
    const startTime = process.hrtime.bigint();
    logger.trace(`Starting file collection for ${filePaths.length} files using worker pool`);

    let completedTasks = 0;
    const totalTasks = tasks.length;

    const results = await Promise.all(
      tasks.map((task) =>
        runTask(task).then((result) => {
          completedTasks++;
          progressCallback(`Collect file... (${completedTasks}/${totalTasks}) ${pc.dim(task.filePath)}`);
          logger.trace(`Collect files... (${completedTasks}/${totalTasks}) ${task.filePath}`);
          return result;
        }),
      ),
    );

    const endTime = process.hrtime.bigint();
    const duration = Number(endTime - startTime) / 1e6;
    logger.trace(`File collection completed in ${duration.toFixed(2)}ms`);

    return results.filter((file): file is RawFile => file !== null);
  } catch (error) {
    logger.error('Error during file collection:', error);
    throw error;
  }
};

```

## /src/core/file/fileManipulate.ts

```ts path="/src/core/file/fileManipulate.ts" 
import path from 'node:path';
import strip from 'strip-comments';

export interface FileManipulator {
  removeComments(content: string): string;
  removeEmptyLines(content: string): string;
}

const rtrimLines = (content: string): string =>
  content
    .split('\n')
    .map((line) => line.trimEnd())
    .join('\n');

class BaseManipulator implements FileManipulator {
  removeComments(content: string): string {
    return content;
  }

  removeEmptyLines(content: string): string {
    return content
      .split('\n')
      .filter((line) => line.trim() !== '')
      .join('\n');
  }
}

class StripCommentsManipulator extends BaseManipulator {
  private language: string;

  constructor(language: string) {
    super();
    this.language = language;
  }

  removeComments(content: string): string {
    const result = strip(content, {
      language: this.language,
      preserveNewlines: true,
    });
    return rtrimLines(result);
  }
}

class CppManipulator extends BaseManipulator {
  removeComments(content: string): string {
    let result = strip(content, {
      language: 'c',
      preserveNewlines: true,
    });

    result = result
      .split('\n')
      .map((line) => {
        const tripleSlashIndex = line.indexOf('///');
        if (tripleSlashIndex !== -1) {
          return line.substring(0, tripleSlashIndex).trimEnd();
        }
        return line;
      })
      .join('\n');

    return rtrimLines(result);
  }
}

class PythonManipulator extends BaseManipulator {
  removeDocStrings(content: string): string {
    if (!content) return '';
    const lines = content.split('\n');

    let result = '';

    let buffer = '';
    let quoteType: '' | "'" | '"' = '';
    let tripleQuotes = 0;

    const doubleQuoteRegex = /^\s*(?<!\\)(?:""")\s*(?:\n)?[\s\S]*?(?<!("""))(?<!\\)(?:""")/gm;
    const singleQuoteRegex = /^\s*(?<!\\)(?:''')\s*(?:\n)?[\s\S]*?(?<!('''))(?<!\\)(?:''')/gm;

    const sz = lines.length;
    for (let i = 0; i < sz; i++) {
      const line = lines[i] + (i !== sz - 1 ? '\n' : '');
      buffer += line;
      if (quoteType === '') {
        const indexSingle = line.search(/(?<![\"])(?<!\\)'''(?![\"])/g);
        const indexDouble = line.search(/(?<![\'])(?<!\\)"""(?![\'])/g);
        if (indexSingle !== -1 && (indexDouble === -1 || indexSingle < indexDouble)) {
          quoteType = "'";
        } else if (indexDouble !== -1 && (indexSingle === -1 || indexDouble < indexSingle)) {
          quoteType = '"';
        }
      }
      if (quoteType === "'") {
        tripleQuotes += (line.match(/(?<![\"])(?<!\\)'''(?!["])/g) || []).length;
      }
      if (quoteType === '"') {
        tripleQuotes += (line.match(/(?<![\'])(?<!\\)"""(?![\'])/g) || []).length;
      }

      if (tripleQuotes % 2 === 0) {
        const docstringRegex = quoteType === '"' ? doubleQuoteRegex : singleQuoteRegex;
        buffer = buffer.replace(docstringRegex, '');
        result += buffer;
        buffer = '';
        tripleQuotes = 0;
        quoteType = '';
      }
    }

    result += buffer;
    return result;
  }

  removeHashComments(content: string): string {
    const searchInPairs = (pairs: [number, number][], hashIndex: number): boolean => {
      return pairs.some(([start, end]) => hashIndex > start && hashIndex < end);
    };

    let result = '';
    const pairs: [number, number][] = [];
    let prevQuote = 0;
    while (prevQuote < content.length) {
      const openingQuote = content.slice(prevQuote + 1).search(/(?<!\\)(?:"|'|'''|""")/g) + prevQuote + 1;
      if (openingQuote === prevQuote) break;

      let closingQuote = -1;
      if (content.startsWith('"""', openingQuote) || content.startsWith("'''", openingQuote)) {
        const quoteType = content.slice(openingQuote, openingQuote + 3);
        closingQuote = content.indexOf(quoteType, openingQuote + 3);
      } else {
        const quoteType = content[openingQuote];
        closingQuote = content.indexOf(quoteType, openingQuote + 1);
      }

      if (closingQuote === -1) break;
      pairs.push([openingQuote, closingQuote]);
      prevQuote = closingQuote;
    }
    let prevHash = 0;
    while (prevHash < content.length) {
      const hashIndex = content.slice(prevHash).search(/(?<!\\)#/g) + prevHash;
      if (hashIndex === prevHash - 1) {
        result += content.slice(prevHash);
        break;
      }

      const isInsideString = searchInPairs(pairs, hashIndex);
      const nextNewLine = content.indexOf('\n', hashIndex);

      if (!isInsideString) {
        if (nextNewLine === -1) {
          result += content.slice(prevHash);
          break;
        }
        result += `${content.slice(prevHash, hashIndex)}\n`;
      } else {
        if (nextNewLine === -1) {
          result += content.slice(prevHash);
          break;
        }
        result += `${content.slice(prevHash, nextNewLine)}\n`;
      }

      prevHash = nextNewLine + 1;
    }
    return result;
  }

  removeComments(content: string): string {
    let result = this.removeDocStrings(content);
    result = this.removeHashComments(result);
    return rtrimLines(result);
  }
}

class CompositeManipulator extends BaseManipulator {
  private manipulators: FileManipulator[];

  constructor(...manipulators: FileManipulator[]) {
    super();
    this.manipulators = manipulators;
  }

  removeComments(content: string): string {
    return this.manipulators.reduce((acc, manipulator) => manipulator.removeComments(acc), content);
  }
}

const manipulators: Record<string, FileManipulator> = {
  '.c': new StripCommentsManipulator('c'),
  '.h': new StripCommentsManipulator('c'),
  '.hpp': new CppManipulator(),
  '.cpp': new CppManipulator(),
  '.cc': new CppManipulator(),
  '.cxx': new CppManipulator(),
  '.cs': new StripCommentsManipulator('csharp'),
  '.css': new StripCommentsManipulator('css'),
  '.dart': new StripCommentsManipulator('c'),
  '.go': new StripCommentsManipulator('c'),
  '.html': new StripCommentsManipulator('html'),
  '.java': new StripCommentsManipulator('java'),
  '.js': new StripCommentsManipulator('javascript'),
  '.jsx': new StripCommentsManipulator('javascript'),
  '.kt': new StripCommentsManipulator('c'),
  '.less': new StripCommentsManipulator('less'),
  '.php': new StripCommentsManipulator('php'),
  '.rb': new StripCommentsManipulator('ruby'),
  '.rs': new StripCommentsManipulator('c'),
  '.sass': new StripCommentsManipulator('sass'),
  '.scss': new StripCommentsManipulator('sass'),
  '.sh': new StripCommentsManipulator('perl'),
  '.sol': new StripCommentsManipulator('c'),
  '.sql': new StripCommentsManipulator('sql'),
  '.swift': new StripCommentsManipulator('swift'),
  '.ts': new StripCommentsManipulator('javascript'),
  '.tsx': new StripCommentsManipulator('javascript'),
  '.xml': new StripCommentsManipulator('xml'),
  '.yaml': new StripCommentsManipulator('perl'),
  '.yml': new StripCommentsManipulator('perl'),

  '.py': new PythonManipulator(),

  '.vue': new CompositeManipulator(
    new StripCommentsManipulator('html'),
    new StripCommentsManipulator('css'),
    new StripCommentsManipulator('javascript'),
  ),
  '.svelte': new CompositeManipulator(
    new StripCommentsManipulator('html'),
    new StripCommentsManipulator('css'),
    new StripCommentsManipulator('javascript'),
  ),
};

export const getFileManipulator = (filePath: string): FileManipulator | null => {
  const ext = path.extname(filePath);
  return manipulators[ext] || null;
};

```

## /src/core/file/filePathSort.ts

```ts path="/src/core/file/filePathSort.ts" 
import path from 'node:path';

// Sort paths for general use (not affected by git change count)
export const sortPaths = (filePaths: string[]): string[] => {
  return filePaths.sort((a, b) => {
    const partsA = a.split(path.sep);
    const partsB = b.split(path.sep);

    for (let i = 0; i < Math.min(partsA.length, partsB.length); i++) {
      if (partsA[i] !== partsB[i]) {
        const isLastA = i === partsA.length - 1;
        const isLastB = i === partsB.length - 1;

        if (!isLastA && isLastB) return -1; // Directory
        if (isLastA && !isLastB) return 1; // File

        return partsA[i].localeCompare(partsB[i]); // Alphabetical order
      }
    }

    // Sort by path length when all parts are equal
    return partsA.length - partsB.length;
  });
};

```

## /src/core/file/fileProcess.ts

```ts path="/src/core/file/fileProcess.ts" 
import pc from 'picocolors';
import type { RepomixConfigMerged } from '../../config/configSchema.js';
import { logger } from '../../shared/logger.js';
import { initPiscina } from '../../shared/processConcurrency.js';
import type { RepomixProgressCallback } from '../../shared/types.js';
import { type FileManipulator, getFileManipulator } from './fileManipulate.js';
import type { ProcessedFile, RawFile } from './fileTypes.js';
import type { FileProcessTask } from './workers/fileProcessWorker.js';

type GetFileManipulator = (filePath: string) => FileManipulator | null;

const initTaskRunner = (numOfTasks: number) => {
  const pool = initPiscina(numOfTasks, new URL('./workers/fileProcessWorker.js', import.meta.url).href);
  return (task: FileProcessTask) => pool.run(task);
};

export const processFiles = async (
  rawFiles: RawFile[],
  config: RepomixConfigMerged,
  progressCallback: RepomixProgressCallback,
  deps: {
    initTaskRunner: typeof initTaskRunner;
    getFileManipulator: GetFileManipulator;
  } = {
    initTaskRunner,
    getFileManipulator,
  },
): Promise<ProcessedFile[]> => {
  const runTask = deps.initTaskRunner(rawFiles.length);
  const tasks = rawFiles.map(
    (rawFile, index) =>
      ({
        rawFile,
        config,
      }) satisfies FileProcessTask,
  );

  try {
    const startTime = process.hrtime.bigint();
    logger.trace(`Starting file processing for ${rawFiles.length} files using worker pool`);

    let completedTasks = 0;
    const totalTasks = tasks.length;

    const results = await Promise.all(
      tasks.map((task) =>
        runTask(task).then((result) => {
          completedTasks++;
          progressCallback(`Processing file... (${completedTasks}/${totalTasks}) ${pc.dim(task.rawFile.path)}`);
          logger.trace(`Processing file... (${completedTasks}/${totalTasks}) ${task.rawFile.path}`);
          return result;
        }),
      ),
    );

    const endTime = process.hrtime.bigint();
    const duration = Number(endTime - startTime) / 1e6;
    logger.trace(`File processing completed in ${duration.toFixed(2)}ms`);

    return results;
  } catch (error) {
    logger.error('Error during file processing:', error);
    throw error;
  }
};

```

## /src/core/file/fileProcessContent.ts

```ts path="/src/core/file/fileProcessContent.ts" 
import type { RepomixConfigMerged } from '../../config/configSchema.js';
import { logger } from '../../shared/logger.js';
import { parseFile } from '../treeSitter/parseFile.js';
import { getFileManipulator } from './fileManipulate.js';
import type { RawFile } from './fileTypes.js';

/**
 * Process the content of a file according to the configuration
 * Applies various transformations based on the config:
 * - Remove comments
 * - Remove empty lines
 * - Compress content using Tree-sitter
 * - Add line numbers
 *
 * @param rawFile Raw file data containing path and content
 * @param config Repomix configuration
 * @returns Processed content string
 */
export const processContent = async (rawFile: RawFile, config: RepomixConfigMerged): Promise<string> => {
  const processStartAt = process.hrtime.bigint();
  let processedContent = rawFile.content;
  const manipulator = getFileManipulator(rawFile.path);

  logger.trace(`Processing file: ${rawFile.path}`);

  if (manipulator && config.output.removeComments) {
    processedContent = manipulator.removeComments(processedContent);
  }

  if (config.output.removeEmptyLines && manipulator) {
    processedContent = manipulator.removeEmptyLines(processedContent);
  }

  processedContent = processedContent.trim();

  if (config.output.compress) {
    try {
      const parsedContent = await parseFile(processedContent, rawFile.path, config);
      if (parsedContent === undefined) {
        logger.trace(`Failed to parse ${rawFile.path} in compressed mode. Using original content.`);
      }
      processedContent = parsedContent ?? processedContent;
    } catch (error: unknown) {
      const message = error instanceof Error ? error.message : String(error);
      logger.error(`Error parsing ${rawFile.path} in compressed mode: ${message}`);
      //re-throw error
      throw error;
    }
  } else if (config.output.showLineNumbers) {
    const lines = processedContent.split('\n');
    const padding = lines.length.toString().length;
    const numberedLines = lines.map((line, i) => `${(i + 1).toString().padStart(padding)}: ${line}`);
    processedContent = numberedLines.join('\n');
  }

  const processEndAt = process.hrtime.bigint();
  logger.trace(`Processed file: ${rawFile.path}. Took: ${(Number(processEndAt - processStartAt) / 1e6).toFixed(2)}ms`);

  return processedContent;
};

```

## /src/core/file/fileRead.ts

```ts path="/src/core/file/fileRead.ts" 
import * as fs from 'node:fs/promises';
import iconv from 'iconv-lite';
import { isBinary } from 'istextorbinary';
import jschardet from 'jschardet';
import { logger } from '../../shared/logger.js';

/**
 * Read a file and return its text content
 * @param filePath Path to the file
 * @param maxFileSize Maximum file size in bytes
 * @returns File content as string, or null if the file is binary or exceeds size limit
 */
export const readRawFile = async (filePath: string, maxFileSize: number): Promise<string | null> => {
  try {
    const stats = await fs.stat(filePath);

    if (stats.size > maxFileSize) {
      const sizeKB = (stats.size / 1024).toFixed(1);
      const maxSizeKB = (maxFileSize / 1024).toFixed(1);
      logger.trace(`File exceeds size limit: ${sizeKB}KB > ${maxSizeKB}KB (${filePath})`);
      return null;
    }

    if (isBinary(filePath)) {
      logger.debug(`Skipping binary file: ${filePath}`);
      return null;
    }

    logger.trace(`Reading file: ${filePath}`);

    const buffer = await fs.readFile(filePath);

    if (isBinary(null, buffer)) {
      logger.debug(`Skipping binary file (content check): ${filePath}`);
      return null;
    }

    const encoding = jschardet.detect(buffer).encoding || 'utf-8';
    const content = iconv.decode(buffer, encoding);

    return content;
  } catch (error) {
    logger.warn(`Failed to read file: ${filePath}`, error);
    return null;
  }
};

```

## /src/core/file/fileSearch.ts

```ts path="/src/core/file/fileSearch.ts" 
import fs from 'node:fs/promises';
import path from 'node:path';
import { globby } from 'globby';
import { minimatch } from 'minimatch';
import type { RepomixConfigMerged } from '../../config/configSchema.js';
import { defaultIgnoreList } from '../../config/defaultIgnore.js';
import { RepomixError } from '../../shared/errorHandle.js';
import { logger } from '../../shared/logger.js';
import { sortPaths } from './filePathSort.js';
import { PermissionError, checkDirectoryPermissions } from './permissionCheck.js';

export interface FileSearchResult {
  filePaths: string[];
  emptyDirPaths: string[];
}

const findEmptyDirectories = async (
  rootDir: string,
  directories: string[],
  ignorePatterns: string[],
): Promise<string[]> => {
  const emptyDirs: string[] = [];

  for (const dir of directories) {
    const fullPath = path.join(rootDir, dir);
    try {
      const entries = await fs.readdir(fullPath);
      const hasVisibleContents = entries.some((entry) => !entry.startsWith('.'));

      if (!hasVisibleContents) {
        // This checks if the directory itself matches any ignore patterns
        const shouldIgnore = ignorePatterns.some((pattern) => minimatch(dir, pattern) || minimatch(`${dir}/`, pattern));

        if (!shouldIgnore) {
          emptyDirs.push(dir);
        }
      }
    } catch (error) {
      logger.debug(`Error checking directory ${dir}:`, error);
    }
  }

  return emptyDirs;
};

// Check if a path is a git worktree reference file
const isGitWorktreeRef = async (gitPath: string): Promise<boolean> => {
  try {
    const stats = await fs.stat(gitPath);
    if (!stats.isFile()) {
      return false;
    }

    const content = await fs.readFile(gitPath, 'utf8');
    return content.startsWith('gitdir:');
  } catch {
    return false;
  }
};

/**
 * Escapes special characters in glob patterns to handle paths with parentheses.
 * Example: "src/(categories)" -> "src/\\(categories\\)"
 */
export const escapeGlobPattern = (pattern: string): string => {
  // First escape backslashes
  const escapedBackslashes = pattern.replace(/\\/g, '\\\\');
  // Then escape special characters () and [], but NOT {}
  return escapedBackslashes.replace(/[()[\]]/g, '\\{{contextString}}amp;');
};

/**
 * Normalizes glob patterns by removing trailing slashes and ensuring consistent directory pattern handling.
 * Makes "**\/folder", "**\/folder/", and "**\/folder/**\/*" behave identically.
 *
 * @param pattern The glob pattern to normalize
 * @returns The normalized pattern
 */
export const normalizeGlobPattern = (pattern: string): string => {
  // Remove trailing slash but preserve patterns that end with "**/"
  if (pattern.endsWith('/') && !pattern.endsWith('**/')) {
    return pattern.slice(0, -1);
  }

  // Convert **/folder to **/folder/** for consistent ignore pattern behavior
  if (pattern.startsWith('**/') && !pattern.includes('/**')) {
    return `${pattern}/**`;
  }

  return pattern;
};

// Get all file paths considering the config
export const searchFiles = async (rootDir: string, config: RepomixConfigMerged): Promise<FileSearchResult> => {
  // First check directory permissions
  const permissionCheck = await checkDirectoryPermissions(rootDir);

  if (permissionCheck.details?.read !== true) {
    if (permissionCheck.error instanceof PermissionError) {
      throw permissionCheck.error;
    }
    throw new RepomixError(
      `Target directory is not readable or does not exist. Please check folder access permissions for your terminal app.\npath: ${rootDir}`,
    );
  }

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

  try {
    const [ignorePatterns, ignoreFilePatterns] = await Promise.all([
      getIgnorePatterns(rootDir, config),
      getIgnoreFilePatterns(config),
    ]);

    // 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);

    // Check if .git is a worktree reference
    const gitPath = path.join(rootDir, '.git');
    const isWorktree = await isGitWorktreeRef(gitPath);

    // Modify ignore patterns for git worktree
    const adjustedIgnorePatterns = [...normalizedIgnorePatterns];
    if (isWorktree) {
      // Remove '.git/**' pattern and add '.git' to ignore the reference file
      const gitIndex = adjustedIgnorePatterns.indexOf('.git/**');
      if (gitIndex !== -1) {
        adjustedIgnorePatterns.splice(gitIndex, 1);
        adjustedIgnorePatterns.push('.git');
      }
    }

    const filePaths = await globby(includePatterns, {
      cwd: rootDir,
      ignore: [...adjustedIgnorePatterns],
      ignoreFiles: [...ignoreFilePatterns],
      onlyFiles: true,
      absolute: false,
      dot: true,
      followSymbolicLinks: false,
    }).catch((error) => {
      // Handle EPERM errors specifically
      if (error.code === 'EPERM' || error.code === 'EACCES') {
        throw new PermissionError(
          `Permission denied while scanning directory. Please check folder access permissions for your terminal app. path: ${rootDir}`,
          rootDir,
        );
      }
      throw error;
    });

    let emptyDirPaths: string[] = [];
    if (config.output.includeEmptyDirectories) {
      const directories = await globby(includePatterns, {
        cwd: rootDir,
        ignore: [...adjustedIgnorePatterns],
        ignoreFiles: [...ignoreFilePatterns],
        onlyDirectories: true,
        absolute: false,
        dot: true,
        followSymbolicLinks: false,
      });

      emptyDirPaths = await findEmptyDirectories(rootDir, directories, adjustedIgnorePatterns);
    }

    logger.trace(`Filtered ${filePaths.length} files`);

    return {
      filePaths: sortPaths(filePaths),
      emptyDirPaths: sortPaths(emptyDirPaths),
    };
  } catch (error: unknown) {
    // Re-throw PermissionError as is
    if (error instanceof PermissionError) {
      throw error;
    }

    if (error instanceof Error) {
      logger.error('Error filtering files:', error.message);
      throw new Error(`Failed to filter files in directory ${rootDir}. Reason: ${error.message}`);
    }

    logger.error('An unexpected error occurred:', error);
    throw new Error('An unexpected error occurred while filtering files.');
  }
};

export const parseIgnoreContent = (content: string): string[] => {
  if (!content) return [];

  return content.split('\n').reduce<string[]>((acc, line) => {
    const trimmedLine = line.trim();
    if (trimmedLine && !trimmedLine.startsWith('#')) {
      acc.push(trimmedLine);
    }
    return acc;
  }, []);
};

export const getIgnoreFilePatterns = async (config: RepomixConfigMerged): Promise<string[]> => {
  const ignoreFilePatterns: string[] = [];

  if (config.ignore.useGitignore) {
    ignoreFilePatterns.push('**/.gitignore');
  }

  ignoreFilePatterns.push('**/.repomixignore');

  return ignoreFilePatterns;
};

export const getIgnorePatterns = async (rootDir: string, config: RepomixConfigMerged): Promise<string[]> => {
  const ignorePatterns = new Set<string>();

  // Add default ignore patterns
  if (config.ignore.useDefaultPatterns) {
    logger.trace('Adding default ignore patterns');
    for (const pattern of defaultIgnoreList) {
      ignorePatterns.add(pattern);
    }
  }

  // Add repomix output file
  if (config.output.filePath) {
    const absoluteOutputPath = path.resolve(config.cwd, config.output.filePath);
    const relativeToTargetPath = path.relative(rootDir, absoluteOutputPath);

    logger.trace('Adding output file to ignore patterns:', relativeToTargetPath);

    ignorePatterns.add(relativeToTargetPath);
  }

  // Add custom ignore patterns
  if (config.ignore.customPatterns) {
    logger.trace('Adding custom ignore patterns:', config.ignore.customPatterns);
    for (const pattern of config.ignore.customPatterns) {
      ignorePatterns.add(pattern);
    }
  }

  // Add patterns from .git/info/exclude if useGitignore is enabled
  if (config.ignore.useGitignore) {
    const excludeFilePath = path.join(rootDir, '.git', 'info', 'exclude');

    try {
      const excludeFileContent = await fs.readFile(excludeFilePath, 'utf8');
      const excludePatterns = parseIgnoreContent(excludeFileContent);

      for (const pattern of excludePatterns) {
        ignorePatterns.add(pattern);
      }
    } catch (error) {
      // File might not exist or might not be accessible, which is fine
      logger.trace('Could not read .git/info/exclude file:', error instanceof Error ? error.message : String(error));
    }
  }

  return Array.from(ignorePatterns);
};

```

## /src/core/file/fileTreeGenerate.ts

```ts path="/src/core/file/fileTreeGenerate.ts" 
import nodepath from 'node:path';

export interface TreeNode {
  name: string;
  children: TreeNode[];
  isDirectory: boolean;
}

const createTreeNode = (name: string, isDirectory: boolean): TreeNode => ({ name, children: [], isDirectory });

export const generateFileTree = (files: string[], emptyDirPaths: string[] = []): TreeNode => {
  const root: TreeNode = createTreeNode('root', true);

  for (const file of files) {
    addPathToTree(root, file, false);
  }

  // Add empty directories
  for (const dir of emptyDirPaths) {
    addPathToTree(root, dir, true);
  }

  return root;
};

const addPathToTree = (root: TreeNode, path: string, isDirectory: boolean): void => {
  const parts = path.split(nodepath.sep);
  let currentNode = root;

  for (let i = 0; i < parts.length; i++) {
    const part = parts[i];
    const isLastPart = i === parts.length - 1;
    let child = currentNode.children.find((c) => c.name === part);

    if (!child) {
      child = createTreeNode(part, !isLastPart || isDirectory);
      currentNode.children.push(child);
    }

    currentNode = child;
  }
};

const sortTreeNodes = (node: TreeNode) => {
  node.children.sort((a, b) => {
    if (a.isDirectory === b.isDirectory) {
      return a.name.localeCompare(b.name);
    }
    return a.isDirectory ? -1 : 1;
  });

  for (const child of node.children) {
    sortTreeNodes(child);
  }
};

export const treeToString = (node: TreeNode, prefix = ''): string => {
  sortTreeNodes(node);
  let result = '';

  for (const child of node.children) {
    result += `${prefix}${child.name}${child.isDirectory ? '/' : ''}\n`;
    if (child.isDirectory) {
      result += treeToString(child, `${prefix}  `);
    }
  }

  return result;
};

export const generateTreeString = (files: string[], emptyDirPaths: string[] = []): string => {
  const tree = generateFileTree(files, emptyDirPaths);
  return treeToString(tree).trim();
};

```

## /src/core/file/fileTypes.ts

```ts path="/src/core/file/fileTypes.ts" 
export interface RawFile {
  path: string;
  content: string;
}

export interface ProcessedFile {
  path: string;
  content: string;
}

```

## /src/core/file/packageJsonParse.ts

```ts path="/src/core/file/packageJsonParse.ts" 
import * as fs from 'node:fs/promises';
import path from 'node:path';
import * as url from 'node:url';
import { logger } from '../../shared/logger.js';

export const getVersion = async (): Promise<string> => {
  try {
    const packageJson = await parsePackageJson();

    if (!packageJson.version) {
      logger.warn('No version found in package.json');
      return 'unknown';
    }

    return packageJson.version;
  } catch (error) {
    logger.error('Error reading package.json:', error);
    return 'unknown';
  }
};

const parsePackageJson = async (): Promise<{
  name: string;
  version: string;
}> => {
  const dirName = url.fileURLToPath(new URL('.', import.meta.url));
  const packageJsonPath = path.join(dirName, '..', '..', '..', 'package.json');
  const packageJsonFile = await fs.readFile(packageJsonPath, 'utf-8');
  const packageJson = JSON.parse(packageJsonFile);
  return packageJson;
};

```

## /src/core/file/permissionCheck.ts

```ts path="/src/core/file/permissionCheck.ts" 
import { constants } from 'node:fs';
import * as fs from 'node:fs/promises';
import { platform } from 'node:os';
import { logger } from '../../shared/logger.js';

export interface PermissionCheckResult {
  hasAllPermission: boolean;
  error?: Error;
  details?: {
    read?: boolean;
    write?: boolean;
    execute?: boolean;
  };
}

export class PermissionError extends Error {
  constructor(
    message: string,
    public readonly path: string,
    public readonly code?: string,
  ) {
    super(message);
    this.name = 'PermissionError';
  }
}

export const checkDirectoryPermissions = async (dirPath: string): Promise<PermissionCheckResult> => {
  try {
    // First try to read directory contents
    await fs.readdir(dirPath);

    // Check specific permissions
    const details = {
      read: false,
      write: false,
      execute: false,
    };

    try {
      await fs.access(dirPath, constants.R_OK);
      details.read = true;
    } catch {}

    try {
      await fs.access(dirPath, constants.W_OK);
      details.write = true;
    } catch {}

    try {
      await fs.access(dirPath, constants.X_OK);
      details.execute = true;
    } catch {}

    const hasAllPermissions = details.read && details.write && details.execute;

    if (!hasAllPermissions) {
      return {
        hasAllPermission: false,
        details,
      };
    }

    return {
      hasAllPermission: true,
      details,
    };
  } catch (error) {
    if (error instanceof Error && 'code' in error) {
      switch (error.code) {
        case 'EPERM':
        case 'EACCES':
        case 'EISDIR':
          return {
            hasAllPermission: false,
            error: new PermissionError(getMacOSPermissionMessage(dirPath, error.code), dirPath, error.code),
          };
        default:
          logger.debug('Directory permission check error:', error);
          return {
            hasAllPermission: false,
            error: error as Error,
          };
      }
    }
    return {
      hasAllPermission: false,
      error: error instanceof Error ? error : new Error(String(error)),
    };
  }
};

const getMacOSPermissionMessage = (dirPath: string, errorCode?: string): string => {
  if (platform() === 'darwin') {
    return `Permission denied: Cannot access '${dirPath}', error code: ${errorCode}.

This error often occurs when macOS security restrictions prevent access to the directory.
To fix this:

1. Open System Settings
2. Navigate to Privacy & Security > Files and Folders
3. Find your terminal app (Terminal.app, iTerm2, VS Code, etc.)
4. Grant necessary folder access permissions

If your terminal app is not listed:
- Try running repomix command again
- When prompted by macOS, click "Allow"
- Restart your terminal app if needed
`;
  }

  return `Permission denied: Cannot access '${dirPath}'`;
};

```

## /src/core/file/workers/fileCollectWorker.ts

```ts path="/src/core/file/workers/fileCollectWorker.ts" 
import path from 'node:path';
import { logger, setLogLevelByEnv } from '../../../shared/logger.js';
import { readRawFile } from '../fileRead.js';

export interface FileCollectTask {
  filePath: string;
  rootDir: string;
  maxFileSize: number;
}

// Set logger log level from environment variable if provided
setLogLevelByEnv();

export default async ({ filePath, rootDir, maxFileSize }: FileCollectTask) => {
  const fullPath = path.resolve(rootDir, filePath);
  const content = await readRawFile(fullPath, maxFileSize);

  if (content) {
    return {
      path: filePath,
      content,
    };
  }

  return null;
};

```

## /src/core/file/workers/fileProcessWorker.ts

```ts path="/src/core/file/workers/fileProcessWorker.ts" 
import type { RepomixConfigMerged } from '../../../config/configSchema.js';
import { logger, setLogLevelByEnv } from '../../../shared/logger.js';
import { processContent } from '../fileProcessContent.js';
import type { ProcessedFile, RawFile } from '../fileTypes.js';

export interface FileProcessTask {
  rawFile: RawFile;
  config: RepomixConfigMerged;
}

// Set logger log level from environment variable if provided
setLogLevelByEnv();

export default async ({ rawFile, config }: FileProcessTask): Promise<ProcessedFile> => {
  const processedContent = await processContent(rawFile, config);
  return {
    path: rawFile.path,
    content: processedContent,
  };
};

```

## /src/core/git/gitCommand.ts

```ts path="/src/core/git/gitCommand.ts" 
import { execFile } from 'node:child_process';
import fs from 'node:fs/promises';
import path from 'node:path';
import { promisify } from 'node:util';
import { RepomixError } from '../../shared/errorHandle.js';
import { logger } from '../../shared/logger.js';

const execFileAsync = promisify(execFile);

export const execGitLogFilenames = async (
  directory: string,
  maxCommits = 100,
  deps = {
    execFileAsync,
  },
): Promise<string[]> => {
  try {
    const result = await deps.execFileAsync('git', [
      '-C',
      directory,
      'log',
      '--pretty=format:',
      '--name-only',
      '-n',
      maxCommits.toString(),
    ]);

    return result.stdout.split('\n').filter(Boolean);
  } catch (error) {
    logger.trace('Failed to get git log filenames:', (error as Error).message);
    return [];
  }
};

export const execGitDiff = async (
  directory: string,
  options: string[] = [],
  deps = {
    execFileAsync,
  },
): Promise<string> => {
  try {
    const result = await deps.execFileAsync('git', [
      '-C',
      directory,
      'diff',
      '--no-color', // Avoid ANSI color codes
      ...options,
    ]);

    return result.stdout || '';
  } catch (error) {
    logger.trace('Failed to execute git diff:', (error as Error).message);
    throw error;
  }
};

export const execGitVersion = async (
  deps = {
    execFileAsync,
  },
): Promise<string> => {
  try {
    const result = await deps.execFileAsync('git', ['--version']);
    return result.stdout || '';
  } catch (error) {
    logger.trace('Failed to execute git version:', (error as Error).message);
    throw error;
  }
};

export const execGitRevParse = async (
  directory: string,
  deps = {
    execFileAsync,
  },
): Promise<string> => {
  try {
    const result = await deps.execFileAsync('git', ['-C', directory, 'rev-parse', '--is-inside-work-tree']);
    return result.stdout || '';
  } catch (error) {
    logger.trace('Failed to execute git rev-parse:', (error as Error).message);
    throw error;
  }
};

export const execLsRemote = async (
  url: string,
  deps = {
    execFileAsync,
  },
): Promise<string> => {
  validateGitUrl(url);

  try {
    const result = await deps.execFileAsync('git', ['ls-remote', '--heads', '--tags', url]);
    return result.stdout || '';
  } catch (error) {
    logger.trace('Failed to execute git ls-remote:', (error as Error).message);
    throw error;
  }
};

export const execGitShallowClone = async (
  url: string,
  directory: string,
  remoteBranch?: string,
  deps = {
    execFileAsync,
  },
) => {
  validateGitUrl(url);

  if (remoteBranch) {
    await deps.execFileAsync('git', ['-C', directory, 'init']);
    await deps.execFileAsync('git', ['-C', directory, 'remote', 'add', 'origin', url]);
    try {
      await deps.execFileAsync('git', ['-C', directory, 'fetch', '--depth', '1', 'origin', remoteBranch]);
      await deps.execFileAsync('git', ['-C', directory, 'checkout', 'FETCH_HEAD']);
    } catch (err: unknown) {
      // git fetch --depth 1 origin <short SHA> always throws "couldn't find remote ref" error
      const isRefNotfoundError =
        err instanceof Error && err.message.includes(`couldn't find remote ref ${remoteBranch}`);

      if (!isRefNotfoundError) {
        // Rethrow error as nothing else we can do
        throw err;
      }

      // Short SHA detection - matches a hexadecimal string of 4 to 39 characters
      // If the string matches this regex, it MIGHT be a short SHA
      // If the string doesn't match, it is DEFINITELY NOT a short SHA
      const isNotShortSHA = !remoteBranch.match(/^[0-9a-f]{4,39}$/i);

      if (isNotShortSHA) {
        // Rethrow error as nothing else we can do
        throw err;
      }

      // Maybe the error is due to a short SHA, let's try again
      // Can't use --depth 1 here as we need to fetch the specific commit
      await deps.execFileAsync('git', ['-C', directory, 'fetch', 'origin']);
      await deps.execFileAsync('git', ['-C', directory, 'checkout', remoteBranch]);
    }
  } else {
    await deps.execFileAsync('git', ['clone', '--depth', '1', url, directory]);
  }

  // Clean up .git directory
  await fs.rm(path.join(directory, '.git'), { recursive: true, force: true });
};

/**
 * Validates a Git URL for security and format
 * @throws {RepomixError} If the URL is invalid or contains potentially dangerous parameters
 */
export const validateGitUrl = (url: string): void => {
  if (url.includes('--upload-pack') || url.includes('--config') || url.includes('--exec')) {
    throw new RepomixError(`Invalid repository URL. URL contains potentially dangerous parameters: ${url}`);
  }

  // Check if the URL starts with git@ or https://
  if (!(url.startsWith('git@') || url.startsWith('https://'))) {
    throw new RepomixError(`Invalid URL protocol for '${url}'. URL must start with 'git@' or 'https://'`);
  }

  try {
    if (url.startsWith('https://')) {
      new URL(url);
    }
  } catch (error) {
    throw new RepomixError(`Invalid repository URL. Please provide a valid URL: ${url}`);
  }
};

```

## /src/core/git/gitDiffHandle.ts

```ts path="/src/core/git/gitDiffHandle.ts" 
import type { RepomixConfigMerged } from '../../config/configSchema.js';
import { RepomixError } from '../../shared/errorHandle.js';
import { logger } from '../../shared/logger.js';
import { execGitDiff } from './gitCommand.js';
import { isGitRepository } from './gitRepositoryHandle.js';

export interface GitDiffResult {
  workTreeDiffContent: string;
  stagedDiffContent: string;
}

export const getWorkTreeDiff = async (
  directory: string,
  deps = {
    execGitDiff,
    isGitRepository,
  },
): Promise<string> => {
  return getDiff(directory, [], deps);
};

export const getStagedDiff = async (
  directory: string,
  deps = {
    execGitDiff,
    isGitRepository,
  },
): Promise<string> => {
  return getDiff(directory, ['--cached'], deps);
};

/**
 * Helper function to get git diff with common repository check and error handling
 */
const getDiff = async (
  directory: string,
  options: string[],
  deps = {
    execGitDiff,
    isGitRepository,
  },
): Promise<string> => {
  try {
    // Check if the directory is a git repository
    const isGitRepo = await deps.isGitRepository(directory);
    if (!isGitRepo) {
      logger.trace('Not a git repository, skipping diff generation');
      return '';
    }

    // Get the diff with provided options
    const result = await deps.execGitDiff(directory, options);

    return result;
  } catch (error) {
    logger.trace('Failed to get git diff:', (error as Error).message);
    return '';
  }
};

export const getGitDiffs = async (
  rootDirs: string[],
  config: RepomixConfigMerged,
  deps = {
    getWorkTreeDiff,
    getStagedDiff,
  },
): Promise<GitDiffResult | undefined> => {
  // Get git diffs if enabled
  let gitDiffResult: GitDiffResult | undefined;

  if (config.output.git?.includeDiffs) {
    try {
      // Use the first directory as the git repository root
      // Usually this would be the root of the project
      const gitRoot = rootDirs[0] || config.cwd;
      const [workTreeDiffContent, stagedDiffContent] = await Promise.all([
        deps.getWorkTreeDiff(gitRoot),
        deps.getStagedDiff(gitRoot),
      ]);

      gitDiffResult = {
        workTreeDiffContent,
        stagedDiffContent,
      };
    } catch (error) {
      if (error instanceof Error) {
        throw new RepomixError(`Failed to get git diffs: ${error.message}`);
      }
    }
  }

  return gitDiffResult;
};

```

## /src/core/metrics/workers/types.ts

```ts path="/src/core/metrics/workers/types.ts" 
export interface FileMetrics {
  path: string;
  charCount: number;
  tokenCount: number;
}

```


The content has been capped at 50000 tokens. The user could consider applying other filters to refine the result. The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.
Copied!