{"_id":"@condu-feature/auto-package-exports","_rev":"4-77a6eba6143a1921ace5bbafc8b9906c","name":"@condu-feature/auto-package-exports","dist-tags":{"latest":"1.0.3"},"versions":{"1.0.0":{"name":"@condu-feature/auto-package-exports","version":"1.0.0","keywords":["condu","feature","package.json","exports"],"_id":"@condu-feature/auto-package-exports@1.0.0","maintainers":[{"name":"niieani","email":"npm@invent.life"}],"homepage":"https://github.com/niieani/condu#readme","bugs":{"url":"https://github.com/niieani/condu/issues"},"dist":{"shasum":"51e5496ba512f7fc21e7f7f818e3aabc2dfde8ca","tarball":"https://registry.npmjs.org/@condu-feature/auto-package-exports/-/auto-package-exports-1.0.0.tgz","fileCount":23,"integrity":"sha512-LIUgGJrOfnviGHmdiFqmmXRl/of6angCi8yT2fgDZ4qEMoeikNAdmwVd2xO0chXrNFLha1ZNhZPfeVH9pe6kVA==","signatures":[{"sig":"MEUCIB8khfu5uW0VcJw/aEJXhg/zcuSNZ2tuDrPoBXa6mTwNAiEAnnUaBNIFwQGl3RLkcLMll+egfuB+uaxosgVPyPEWDkI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@condu-feature%2fauto-package-exports@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":79674},"type":"module","exports":{".":{"bun":"./autoPackageExports.ts","import":"./autoPackageExports.js","source":"./autoPackageExports.ts","default":"./autoPackageExports.js","require":"./autoPackageExports.cjs","@condu-feature":"./autoPackageExports.ts"},"./*.js":{"bun":"./*.ts","import":"./*.js","default":"./*.js","require":"./*.cjs"},"./*.json":"./*.json"},"gitHead":"4cf982c9a335ff483ad14d530551ab648c2286c8","scripts":{},"_npmUser":{"name":"niieani","email":"npm@invent.life"},"repository":{"url":"git+https://github.com/niieani/condu.git","type":"git","directory":"packages/features/auto-package-exports"},"_npmVersion":"11.3.0","description":"Condu feature for auto-generating package.json exports","directories":{},"_nodeVersion":"24.1.0","dependencies":{"condu":"1.0.0","@condu/workspace-utils":"1.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@condu-test/utils":"1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/auto-package-exports_1.0.0_1754096722985_0.9059834352201943","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@condu-feature/auto-package-exports","version":"1.0.1","keywords":["condu","feature","package.json","exports"],"_id":"@condu-feature/auto-package-exports@1.0.1","maintainers":[{"name":"niieani","email":"npm@invent.life"}],"homepage":"https://github.com/niieani/condu#readme","bugs":{"url":"https://github.com/niieani/condu/issues"},"dist":{"shasum":"73253b0391e3c29360ef071db256a1fbaf1b4732","tarball":"https://registry.npmjs.org/@condu-feature/auto-package-exports/-/auto-package-exports-1.0.1.tgz","fileCount":23,"integrity":"sha512-rds5WA8x3pTuS541/UKxtpGks9nM8T0KGK525VHAkkKB6NnTFc8uajA/7CmXWRJhSIgs/kJgC0K58a4Z+1rCOw==","signatures":[{"sig":"MEYCIQDscQUvJ+iOcIBA2yQa9lgxznBfNjBw8JkpWzn5muj1jQIhALcfUVVck7Feobyg5pWXqhRoR/uM7PDvrqYkIedVbesV","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@condu-feature%2fauto-package-exports@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":79944},"type":"module","exports":{".":{"bun":"./autoPackageExports.ts","import":"./autoPackageExports.js","source":"./autoPackageExports.ts","default":"./autoPackageExports.js","require":"./autoPackageExports.cjs","@condu-feature":"./autoPackageExports.ts"},"./*.js":{"bun":"./*.ts","import":"./*.js","default":"./*.js","require":"./*.cjs"},"./*.json":"./*.json"},"gitHead":"d34101d8c8c57b346a21a0e11a303ba44d00547e","scripts":{},"_npmUser":{"name":"niieani","email":"npm@invent.life"},"repository":{"url":"git+https://github.com/niieani/condu.git","type":"git","directory":"packages/features/auto-package-exports"},"_npmVersion":"11.3.0","description":"Condu feature for auto-generating package.json exports","directories":{},"_nodeVersion":"24.1.0","dependencies":{"condu":"1.0.1","@condu/workspace-utils":"1.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@condu-test/utils":"1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/auto-package-exports_1.0.1_1754180766148_0.02874359909433477","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@condu-feature/auto-package-exports","version":"1.0.2","keywords":["condu","feature","package.json","exports"],"_id":"@condu-feature/auto-package-exports@1.0.2","maintainers":[{"name":"niieani","email":"npm@invent.life"}],"homepage":"https://github.com/niieani/condu#readme","bugs":{"url":"https://github.com/niieani/condu/issues"},"dist":{"shasum":"fb5eea84b65262f9676a8d47cb838b7010db688c","tarball":"https://registry.npmjs.org/@condu-feature/auto-package-exports/-/auto-package-exports-1.0.2.tgz","fileCount":23,"integrity":"sha512-YKg57g+J7oFOgXK8aawTHILi4l1sqxqrDV+kDEzE3K1pyIvPAtw8yUDnautEowkFPFrVlmhxCU/MSLe3SAfDjQ==","signatures":[{"sig":"MEYCIQDY1uM6zJg95zuFPVmDVB0q/ysD8DUXJvtF7XwXqhVkGAIhAOu8C3TJlQKz7MHbn8/fdy8mDvdtDc2zNQzpu4lWOzVU","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@condu-feature%2fauto-package-exports@1.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":80259},"type":"module","exports":{".":{"bun":"./autoPackageExports.ts","import":"./autoPackageExports.js","source":"./autoPackageExports.ts","default":"./autoPackageExports.js","require":"./autoPackageExports.cjs","@condu-feature":"./autoPackageExports.ts"},"./*.js":{"bun":"./*.ts","import":"./*.js","default":"./*.js","require":"./*.cjs"},"./*.json":"./*.json"},"gitHead":"46840e3a518063f980f51ee05dd7b8d28c284202","scripts":{},"_npmUser":{"name":"niieani","email":"npm@invent.life"},"repository":{"url":"git+https://github.com/niieani/condu.git","type":"git","directory":"packages/features/auto-package-exports"},"_npmVersion":"11.3.0","description":"Condu feature for auto-generating package.json exports","directories":{},"_nodeVersion":"24.1.0","dependencies":{"condu":"1.0.2","@condu/workspace-utils":"1.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@condu-test/utils":"1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/auto-package-exports_1.0.2_1754860273982_0.300593166499747","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@condu-feature/auto-package-exports","version":"1.0.3","description":"Condu feature for auto-generating package.json exports","keywords":["condu","feature","package.json","exports"],"repository":{"type":"git","url":"git+https://github.com/niieani/condu.git","directory":"packages/features/auto-package-exports"},"type":"module","exports":{".":{"@condu-feature":"./autoPackageExports.ts","source":"./autoPackageExports.ts","bun":"./autoPackageExports.ts","import":"./autoPackageExports.js","require":"./autoPackageExports.cjs","default":"./autoPackageExports.js"},"./*.json":"./*.json","./*.js":{"bun":"./*.ts","import":"./*.js","require":"./*.cjs","default":"./*.js"}},"scripts":{},"dependencies":{"@condu/workspace-utils":"1.0.1","condu":"1.0.3"},"devDependencies":{"@condu-test/utils":"1.0.0"},"publishConfig":{"access":"public"},"_id":"@condu-feature/auto-package-exports@1.0.3","gitHead":"65e30cfae948fee3cb4cd73bb32058f727c403a4","bugs":{"url":"https://github.com/niieani/condu/issues"},"homepage":"https://github.com/niieani/condu#readme","_nodeVersion":"24.1.0","_npmVersion":"11.3.0","dist":{"integrity":"sha512-OdIZZ1UklZM6Bw/MwBEFwqSDOPBjOoVL4xLHJzZfUXsKGeKNGIgony40yXeobqvD76q8WWj65buruCHcXYIBPQ==","shasum":"fe124405a1e40698d4e48c2ac3dee85c8fb52a98","tarball":"https://registry.npmjs.org/@condu-feature/auto-package-exports/-/auto-package-exports-1.0.3.tgz","fileCount":23,"unpackedSize":80529,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@condu-feature%2fauto-package-exports@1.0.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBm1NLnrn4ZIR3FwC1WCvplL5nwW5hMtw67huIQpbYA0AiA09aZMDuwz8lSPHyV9kFOVTcnTOhytu5G+pp4bUw+riw=="}]},"_npmUser":{"name":"niieani","email":"npm@invent.life"},"directories":{},"maintainers":[{"name":"niieani","email":"npm@invent.life"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/auto-package-exports_1.0.3_1754861227327_0.900163669500357"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-02T01:05:22.866Z","modified":"2025-08-10T21:27:07.988Z","1.0.0":"2025-08-02T01:05:23.166Z","1.0.1":"2025-08-03T00:26:06.340Z","1.0.2":"2025-08-10T21:11:14.192Z","1.0.3":"2025-08-10T21:27:07.585Z"},"bugs":{"url":"https://github.com/niieani/condu/issues"},"homepage":"https://github.com/niieani/condu#readme","keywords":["condu","feature","package.json","exports"],"repository":{"type":"git","url":"git+https://github.com/niieani/condu.git","directory":"packages/features/auto-package-exports"},"description":"Condu feature for auto-generating package.json exports","maintainers":[{"name":"niieani","email":"npm@invent.life"}],"readme":"# condu - Configuration as Code\n\ncondu is a configuration management tool for JavaScript/TypeScript projects that solves the \"config hell\" problem by providing a unified approach to manage all project configuration in code.\n\n## Why condu?\n\nModern JavaScript/TypeScript projects require numerous configuration files:\n\n- tsconfig.json\n- eslintrc/eslint.config.js\n- .prettierrc\n- .editorconfig\n- package.json\n- .gitignore\n- And many more...\n\nThese configurations:\n\n- Use different formats (JSON, YAML, JS)\n- Are scattered across your project\n- Are hard to keep in sync across multiple projects\n- Often require changes to multiple files when adding a new tool\n\ncondu solves these problems by:\n\n1. Allowing you to define all configuration in TypeScript\n2. Providing a system to share and reuse configurations\n3. Making it easy to override only what you need\n4. Automating updates across your entire codebase\n\n## Getting Started\n\n### Installation\n\n```bash\n# Using npm\nnpm install condu --save-dev\n\n# Using yarn\nyarn add condu --dev\n\n# Using pnpm\npnpm add condu -D\n```\n\n### Basic Usage\n\n1. Create a `.config/condu.ts` file in your project root:\n\n```typescript\nimport { configure } from \"condu\";\nimport { typescript } from \"@condu-feature/typescript\";\nimport { eslint } from \"@condu-feature/eslint\";\nimport { prettier } from \"@condu-feature/prettier\";\n\nexport default configure({\n  features: [typescript(), eslint(), prettier()],\n});\n```\n\n2. Run condu to apply your configuration:\n\n```bash\nnpx condu apply\n```\n\nThis will generate all the necessary configuration files based on your `.config/condu.ts` file.\n\n## Features\n\nFeatures are the building blocks of condu. Each feature manages configuration for a specific tool or aspect of your project.\n\n### Core Features\n\ncondu comes with many built-in features:\n\n- **typescript**: Manages TypeScript configuration\n- **eslint**: Configures ESLint\n- **prettier**: Sets up Prettier formatting\n- **gitignore**: Creates and manages .gitignore files\n- **vscode**: Configures VS Code workspace settings\n- **editorconfig**: Sets up EditorConfig\n- **pnpm/yarn/npm**: Package manager configuration\n- **moon**: Task runner integration\n- **vitest**: Testing framework setup\n- **release-please**: Release management\n- And more...\n\n### Using Features\n\nEach feature can be configured with options:\n\n```typescript\ntypescript({\n  preset: \"esm-first\",\n  tsconfig: {\n    compilerOptions: {\n      strict: true,\n      skipLibCheck: true,\n    },\n  },\n});\n```\n\n## Monorepo Support\n\ncondu excels at managing monorepo configurations. Define your workspace structure:\n\n```typescript\nexport default configure({\n  projects: [\n    {\n      parentPath: \"packages/features\",\n      nameConvention: \"@myorg/feature-*\",\n    },\n    {\n      parentPath: \"packages/core\",\n      nameConvention: \"@myorg/*\",\n    },\n  ],\n  features: [\n    // ...features\n  ],\n});\n```\n\n## Creating Custom Features\n\nYou can create custom features to encapsulate your own configuration logic.\n\nA feature's primary purpose is to define a _recipe_ - a list of changes that should be made whenever `condu apply` is run. Think of the calls to condu recipe APIs similar to React component hooks.\n\n### Inline Features (Simplest Approach)\n\nFor one-off or simple modifications, you can define features inline directly in your config file:\n\n```typescript\nimport { configure } from \"condu\";\nimport { typescript } from \"@condu-feature/typescript\";\n\nexport default configure({\n  features: [\n    typescript(),\n\n    // Anonymous arrow function feature\n    (condu) => {\n      condu.in({ kind: \"package\" }).modifyPublishedPackageJson((pkg) => ({\n        ...pkg,\n        // Add sideEffects: false to all packages for better tree-shaking\n        sideEffects: false,\n      }));\n    },\n\n    // Named functions will use their name as the feature name\n    function addLicense(condu) {\n      condu.root.generateFile(\"LICENSE\", {\n        content: `MIT License\\n\\nCopyright (c) ${new Date().getFullYear()} My Organization\\n\\n...`,\n      });\n    },\n  ],\n});\n```\n\nInline features:\n\n- Are perfect for quick, project-specific configurations\n- Don't participate in the PeerContext system\n- Are applied in the order they appear in the features array\n\n### Reusable Features with `defineFeature`\n\nFor creating proper reusable features, use the `defineFeature` function:\n\n```typescript\nimport { defineFeature } from \"condu\";\n\nexport const myFeature = (options = {}) =>\n  defineFeature(\"myFeature\", {\n    // The main recipe that runs during configuration application\n    defineRecipe(condu) {\n      // Generate a configuration file\n      condu.root.generateFile(\"my-config.json\", {\n        content: {\n          enabled: options.enabled ?? true,\n          settings: options.settings ?? {},\n        },\n        stringify: JSON.stringify,\n      });\n\n      // Add required dependencies\n      condu.root.ensureDependency(\"my-library\");\n\n      // Target specific packages in a monorepo\n      condu.in({ kind: \"package\" }).modifyPackageJson((pkg) => ({\n        ...pkg,\n        scripts: {\n          ...pkg.scripts,\n          \"my-script\": \"my-command\",\n        },\n      }));\n    },\n  });\n```\n\n### Using PeerContext for Feature Coordination\n\nWhen you want features to influence each other, use the PeerContext system.\nFor example, the TypeScript feature could automatically enable TypeScript-specific ESLint rules as in the example below:\n\n```typescript\n// ESLint feature definition\ndeclare module \"condu\" {\n  interface PeerContext {\n    eslint: {\n      rules: Record<string, unknown>;\n      plugins: string[];\n      extends: string[];\n    };\n  }\n}\n\nexport const eslint = (options = {}) =>\n  defineFeature(\"eslint\", {\n    initialPeerContext: {\n      rules: {\n        \"no-unused-vars\": \"error\",\n      },\n      plugins: [],\n      extends: [\"eslint:recommended\"],\n    },\n\n    defineRecipe(condu, peerContext) {\n      // Generate eslint config using the final peer context\n      // which may have been modified by other features\n      condu.root.generateFile(\".eslintrc.js\", {\n        content: `module.exports = {\n          extends: ${JSON.stringify(peerContext.extends)},\n          plugins: ${JSON.stringify(peerContext.plugins)},\n          rules: ${JSON.stringify(peerContext.rules, null, 2)}\n        }`,\n      });\n\n      // Ensure ESLint dependency\n      condu.root.ensureDependency(\"eslint\");\n\n      // Ensure any plugins are installed\n      for (const plugin of peerContext.plugins) {\n        condu.root.ensureDependency(`eslint-plugin-${plugin}`);\n      }\n    },\n  });\n\n// TypeScript feature that influences ESLint\nexport const typescript = (options = {}) =>\n  defineFeature(\"typescript\", {\n    initialPeerContext: {\n      // TypeScript-specific context\n      config: {\n        strict: true,\n        // ...other TypeScript options\n      },\n    },\n\n    // Here TypeScript feature modifies ESLint's context\n    modifyPeerContexts: (project, initialContext) => ({\n      eslint: (current) => ({\n        ...current,\n        // Add TypeScript ESLint plugin\n        plugins: [...current.plugins, \"typescript\"],\n        // Add TypeScript ESLint config\n        extends: [...current.extends, \"plugin:@typescript-eslint/recommended\"],\n        // Add/modify TypeScript-specific rules\n        rules: {\n          ...current.rules,\n          \"@typescript-eslint/no-explicit-any\": \"error\",\n          \"@typescript-eslint/explicit-function-return-type\": \"warn\",\n          // Disable the base ESLint rule in favor of TypeScript-specific one\n          \"no-unused-vars\": \"off\",\n          \"@typescript-eslint/no-unused-vars\": \"error\",\n        },\n      }),\n    }),\n\n    defineRecipe(condu, peerContext) {\n      // Generate tsconfig.json\n      condu.root.generateFile(\"tsconfig.json\", {\n        content: {\n          compilerOptions: peerContext.config,\n        },\n        stringify: (obj) => JSON.stringify(obj, null, 2),\n      });\n\n      // Ensure TypeScript dependencies\n      condu.root.ensureDependency(\"typescript\");\n\n      // Also add TypeScript ESLint dependencies if ESLint is used\n      if (condu.project.hasFeature(\"eslint\")) {\n        condu.root.ensureDependency(\"@typescript-eslint/parser\");\n        condu.root.ensureDependency(\"@typescript-eslint/eslint-plugin\");\n      }\n    },\n  });\n```\n\nWith this setup:\n\n1. The ESLint feature defines its initial rules and plugin configuration\n2. The TypeScript feature enhances ESLint configuration with TypeScript-specific rules\n3. When both features are used together, you automatically get TypeScript-aware linting\n\n### Advanced Usage: `defineGarnish` for Post-Processing\n\nFor final adjustments after all features have run their main recipes, use `defineGarnish`:\n\n```typescript\nimport { defineFeature } from \"condu\";\n\nexport const packageScripts = () =>\n  defineFeature(\"packageScripts\", {\n    // Standard recipe for basic setup\n    defineRecipe(condu) {\n      // Basic script setup\n      condu.root.modifyPackageJson((pkg) => ({\n        ...pkg,\n        scripts: {\n          ...pkg.scripts,\n          start: \"node index.js\",\n        },\n      }));\n    },\n\n    // Garnish runs after all other features have applied their recipes\n    defineGarnish(condu) {\n      // Access the complete state after all features have run\n      const allTasks = condu.globalRegistry.tasks;\n\n      // Generate scripts based on tasks defined by other features\n      condu.root.modifyPackageJson((pkg) => {\n        const scripts = { ...pkg.scripts };\n\n        // Create aggregate scripts based on task types\n        const buildTasks = allTasks.filter(\n          (task) => task.taskDefinition.type === \"build\",\n        );\n\n        if (buildTasks.length > 0) {\n          scripts[\"build:all\"] = buildTasks\n            .map((t) => `npm run build:${t.taskDefinition.name}`)\n            .join(\" && \");\n\n          // Add individual build scripts for each task\n          for (const task of buildTasks) {\n            scripts[`build:${task.taskDefinition.name}`] =\n              task.taskDefinition.command;\n          }\n        }\n\n        // Create test scripts for all test tasks\n        const testTasks = allTasks.filter(\n          (task) => task.taskDefinition.type === \"test\",\n        );\n\n        if (testTasks.length > 0) {\n          scripts[\"test:all\"] = testTasks\n            .map((t) => `npm run test:${t.taskDefinition.name}`)\n            .join(\" && \");\n        }\n\n        return { ...pkg, scripts };\n      });\n    },\n  });\n```\n\nThe `defineGarnish` function:\n\n- Runs after all features have completed their main recipes\n- Has access to `globalRegistry` with information about all tasks, dependencies, and files\n- Is perfect for generating aggregate configurations or scripts that depend on what other features defined\n- Enables post-processing of files or configurations\n\n## API Reference\n\n### `condu` object\n\nThe main `condu` object available in feature recipes contains the following:\n\n- `condu.project`: Information about the project\n- `condu.root`: Recipe API for the root package\n- `condu.in(criteria)`: Recipe API for the packages matching the criteria\n\nAdditionally, when used in `defineGarnish`:\n\n- `condu.globalRegistry`: Contains the summary of all the recipes, including:\n  - which files were modified\n  - what tasks were registered\n\n### Recipe API\n\nMethods for declaring configuration changes:\n\n#### generateFile\n\nCreates files that are fully managed by condu.\n\n```typescript\ngenerateFile<PathT extends string>(path: PathT, options: GenerateFileOptionsForPath<PathT>): ScopedRecipeApi\n```\n\n- **Purpose**: Generate new files that are completely managed by condu\n- **Features**:\n  - Type-safe content generation\n  - Custom serialization support\n  - File attributes for special handling (e.g., gitignore)\n\n**Examples**:\n\n```typescript\n// Generate a standard JSON configuration file\ncondu.root.generateFile(\"tsconfig.json\", {\n  content: {\n    compilerOptions: {\n      strict: true,\n      target: \"ES2020\",\n    },\n    include: [\"**/*.ts\"],\n  },\n  // Automatically stringify JSON with formatting\n  stringify: (obj) => JSON.stringify(obj, null, 2),\n});\n\n// Generate a YAML file\ncondu.root.generateFile(\"pnpm-workspace.yaml\", {\n  content: {\n    packages: [\"packages/*\"],\n  },\n  // Use a custom YAML stringifier\n  stringify: getYamlStringify(),\n  // Set file attributes for special handling\n  attributes: {\n    gitignore: false, // Don't add to .gitignore\n  },\n});\n\n// Generate a text file with raw content\ncondu.root.generateFile(\".gitignore\", {\n  content: [\"node_modules\", \"build\", \".DS_Store\", \"*.log\"].join(\"\\n\"),\n  // No stringification needed for plain text\n});\n```\n\n#### modifyGeneratedFile\n\nModifies a file that was previously generated by condu.\n\n```typescript\nmodifyGeneratedFile<PathT extends string>(path: PathT, options: ModifyGeneratedFileOptions<ResolvedSerializedType<PathT>>): ScopedRecipeApi\n```\n\n- **Purpose**: Update or extend files already managed by condu\n- **Features**:\n  - Access to current content\n  - Preserves format of the file\n  - Can be used to add or modify portions of existing files in a typesafe way\n  - Can be used without providing a parse/stringify, as it uses the one provided by the feature\n\n**Examples**:\n\n```typescript\n// Modify an existing tsconfig.json\ncondu.root.modifyGeneratedFile(\"tsconfig.json\", {\n  content: ({ content = {} }) => ({\n    ...content,\n    compilerOptions: {\n      ...content.compilerOptions,\n      // Add or update specific compiler options\n      declaration: true,\n      sourceMap: true,\n    },\n  }),\n});\n```\n\n#### modifyUserEditableFile\n\nModifies files that should remain editable by users.\n\n```typescript\nmodifyUserEditableFile<PathT extends string, DeserializedT = ...>(path: PathT, options: ModifyUserEditableFileOptions<DeserializedT>): ScopedRecipeApi\n```\n\n- **Purpose**: Update portions of user-editable files while preserving other user changes\n- **Features**:\n  - Custom parsing and stringification for different formats\n  - Can create the file if it doesn't exist with `ifNotExists: \"create\"`\n  - Preserves content not explicitly modified by condu\n\n**Examples**:\n\n```typescript\n// Modify a JSON file with type safety\ncondu.root.modifyUserEditableFile(\".vscode/settings.json\", {\n  // Get default JSON parsers and stringifiers\n  ...getJsonParseAndStringify<MySettingsType>(),\n  // Create the file if it doesn't exist (that's the default)\n  ifNotExists: \"create\", // other options: \"ignore\" | \"error\"\n  // Modify or provide content\n  content: ({ content = {} }) => ({\n    ...content,\n    // Add or update specific settings while preserving others\n    \"typescript.tsdk\": \"node_modules/typescript/lib\",\n    \"editor.formatOnSave\": true,\n  }),\n});\n\n// Modify a custom format file\ncondu.root.modifyUserEditableFile(\".npmrc\", {\n  // Custom parser for the specific file format\n  parse: (rawContent) => customParse(rawContent),\n  // Custom stringifier for the specific file format\n  stringify: (data) => customStringify(data),\n  // Merge content\n  content: ({ content = {} }) => ({\n    ...content,\n    \"my-setting\": \"value\",\n  }),\n  // Set file attributes (e.g., for .gitignore)\n  attributes: { gitignore: false },\n});\n```\n\n#### ensureDependency\n\nEnsures a dependency is installed in the package.\n\n```typescript\nensureDependency(name: string, dependency?: DependencyDefinitionInput): ScopedRecipeApi\n```\n\n- **Purpose**: Manage dependencies in package.json\n- **Features**:\n  - Installation target customization (dev, peer, regular)\n  - Version specification\n  - Support for aliased packages\n  - Ability to mark as \"built\" for pnpm's `onlyBuiltDependencies`\n\n**Examples**:\n\n```typescript\n// Add a simple dev dependency with default settings\ncondu.root.ensureDependency(\"typescript\");\n\n// Add a dependency with specific options\ncondu.root.ensureDependency(\"react\", {\n  // Specify which dependency list to use\n  list: \"dependencies\",\n  // Specify exact version\n  version: \"18.2.0\",\n  // Use a custom name for the dependency\n  installAsAlias: \"react-aliased\",\n  // Specify how versioning is managed\n  managed: \"version\", // or \"presence\" to preserve existing versions\n});\n\n// Add peer dependencies\ncondu.root.ensureDependency(\"react-dom\", {\n  list: \"peerDependencies\",\n  // Use semver range prefix\n  rangePrefix: \">=\",\n  // Mark as built for pnpm\n  built: true,\n});\n```\n\n#### setDependencyResolutions\n\nSets dependency resolutions to override specific package versions.\n\n```typescript\nsetDependencyResolutions(resolutions: Record<string, string>): ScopedRecipeApi\n```\n\n- **Purpose**: Override dependency versions for all packages in the workspace\n- **Features**:\n  - Works with different package managers (npm, yarn, pnpm)\n  - Adapts to the correct syntax for each package manager\n\n**Examples**:\n\n```typescript\n// Force specific versions of packages\ncondu.root.setDependencyResolutions({\n  lodash: \"4.17.21\",\n  \"webpack/tapable\": \"2.2.1\",\n  \"@types/react\": \"18.0.0\",\n});\n```\n\n#### modifyPackageJson\n\nModifies the package.json file with a custom transformer function.\n\n```typescript\nmodifyPackageJson(modifier: PackageJsonModifier): ScopedRecipeApi\n```\n\n- **Purpose**: Make changes to package.json\n- **Features**:\n  - Full access to the package.json content\n  - Type-safe with package.json type definitions\n  - Can access the global registry state\n\n**Examples**:\n\n```typescript\n// Add custom scripts based on project structure\ncondu.root.modifyPackageJson((pkg) => ({\n  ...pkg,\n  scripts: {\n    ...pkg.scripts,\n    build: \"tsc -p tsconfig.json\",\n    test: \"vitest run\",\n    lint: \"eslint .\",\n  },\n  // Add custom metadata\n  keywords: [...(pkg.keywords || []), \"condu-managed\"],\n}));\n\n// Add or modify specific fields\ncondu.in({ kind: \"package\" }).modifyPackageJson((pkg) => ({\n  ...pkg,\n  // Add TypeScript configuration\n  types: \"./build/index.d.ts\",\n  // Ensure sideEffects flag is set for tree-shaking\n  sideEffects: false,\n}));\n```\n\n#### modifyPublishedPackageJson\n\nModifies the package.json that will be used during publishing.\n\n```typescript\nmodifyPublishedPackageJson(modifier: PackageJsonModifier): ScopedRecipeApi\n```\n\n- **Purpose**: Configure how the package.json appears when published to registries like npm\n- **Features**:\n  - Only affects the published version, not the development version\n  - Perfect for export maps, types path adjustments, etc.\n\n**Examples**:\n\n```typescript\n// Configure exports map for published packages\ncondu.in({ kind: \"package\" }).modifyPublishedPackageJson((pkg) => ({\n  ...pkg,\n  // Add standard entry points\n  main: \"./build/index.js\",\n  module: \"./build/index.js\",\n  types: \"./build/index.d.ts\",\n  // Configure exports map\n  exports: {\n    \".\": {\n      import: \"./build/index.js\",\n      require: \"./build/index.cjs\",\n      types: \"./build/index.d.ts\",\n    },\n    \"./package.json\": \"./package.json\",\n  },\n  // Remove development-only fields\n  devDependencies: undefined,\n}));\n```\n\n#### defineTask\n\nDefines a task that can be run using a task runner.\n\n```typescript\ndefineTask(name: string, taskDefinition: Omit<Task, \"name\">): ScopedRecipeApi\n```\n\n- **Purpose**: Define tasks for build, test, etc. that can be run by task runners or package scripts\n- **Features**:\n  - Task type categorization\n  - Dependencies between tasks\n  - Command definition\n\n**Examples**:\n\n```typescript\n// Define a build task\ncondu.root.defineTask(\"build\", {\n  type: \"build\",\n  command: \"tsc -p tsconfig.json\",\n  inputs: [\"**/*.ts\", \"tsconfig.json\"],\n  outputs: [\"build/**\"],\n});\n\n// Define a test task that depends on the build task\ncondu.root.defineTask(\"test\", {\n  type: \"test\",\n  command: \"vitest run\",\n  deps: [\"build\"],\n});\n```\n\n#### ignoreFile\n\nMarks a file to be ignored by certain tools.\n\n```typescript\nignoreFile(path: string, options?: Omit<PartialGlobalFileAttributes, \"inAllPackages\">): ScopedRecipeApi\n```\n\n- **Purpose**: Add files to gitignore or configure other file attributes without generating content\n- **Features**:\n  - Control file visibility in editors and VCS\n\n**Examples**:\n\n```typescript\n// Add a file to .gitignore\ncondu.root.ignoreFile(\"build/\");\n\n// Configure file attributes\ncondu.root.ignoreFile(\"temp/debug.log\", {\n  gitignore: true,\n  vscode: false, // will still be visible in VSCode\n});\n```\n\n### PeerContext System\n\nThe PeerContext system enables features to share information and coordinate with each other:\n\n1. **Declaring Context**: Features declare what data is exposed and modifiable via TypeScript interface augmentation\n\n```typescript\ndeclare module \"condu\" {\n  interface PeerContext {\n    myFeature: {\n      config: MyConfigType;\n    };\n  }\n}\n```\n\n2. **Initializing Context**: Features provide their initial context data\n\n```typescript\ninitialPeerContext: {\n  config: {\n    /* initial data */\n  }\n}\n```\n\n3. **Modifying Other Contexts**: Features can modify other features' contexts\n\n```typescript\nmodifyPeerContexts: (project, initialContext) => ({\n  otherFeature: (current) => ({\n    ...current,\n    someOption: true,\n  }),\n});\n```\n\n4. **Using Context**: Features get the _final_ (merged) context data passed in to their recipes when they are applied\n\n```typescript\ndefineRecipe(condu, peerContext) {\n  // Use peerContext.config\n}\n```\n\nThis system enables powerful coordination between features without tight coupling.\n\nTo resolve any type-system issues when building a feature that might influence others, be sure to include the peer features as an optional peerDependency, with a broad version requirement (such as `*` or `>=1.0.0`).\n\n## CLI Reference\n\nCondu provides a comprehensive CLI for managing your projects.\n\n### Core Commands\n\n#### `condu init [project-name]`\n\nInitializes a new condu project in the current directory or creates a new directory with the specified name.\n\n```bash\n# Initialize in current directory\ncondu init\n\n# Create a new project directory\ncondu init my-new-project\n```\n\nOptions: None\n\nThe init command will:\n\n- Create a `.config` directory with a default `condu.ts` file\n- Set up a package.json with the necessary dependencies\n- Initialize a git repository if one doesn't exist\n- Add a postinstall script that runs `condu apply`\n\n#### `condu apply`\n\nApplies configuration from your `.config/condu.ts` file, generating or updating all configuration files.\n\n```bash\ncondu apply\n```\n\nOptions: None\n\nThis is the primary command you'll use to apply changes after modifying your condu configuration.\n\n#### `condu create <partial-path> [options]`\n\nCreates a new package in a monorepo according to your project conventions.\n\n```bash\n# Create a basic package\ncondu create features/my-feature\n\n# Create a package with a custom name\ncondu create features/my-feature --as @myorg/custom-name\n```\n\nOptions:\n\n- `--as <name>`: Specify a custom package name\n- `--description <text>`: Add a description to the package.json\n- `--private`: Mark the package as private\n\n#### `condu tsc [options]`\n\nBuilds TypeScript code and additionally creates CommonJS (.cjs) or ES Module (.mjs) versions of your code.\n\n```bash\n# Build with CommonJS output\ncondu tsc --preset ts-to-cts\n\n# Build with ES Module output\ncondu tsc --preset ts-to-mts\n```\n\nOptions:\n\n- `--preset ts-to-cts|ts-to-mts`: Generate CommonJS or ES Module versions\n- All standard TypeScript compiler options are supported\n\n#### `condu release [packages...] [options]`\n\nPrepares packages for release by generating distributable files and optionally publishing to npm.\n\n```bash\n# Release all packages\ncondu release\n\n# Release specific packages\ncondu release @myorg/package1 @myorg/package2\n\n# Do a dry run without publishing\ncondu release --dry-run\n```\n\nOptions:\n\n- `--ci`: Mark non-released packages as private (useful in CI environments)\n- `--npm-tag <tag>`: Specify the npm tag to use (default: latest)\n- `--dry-run`: Prepare packages without actually publishing\n\n#### `condu exec <command> [args...]`\n\nExecutes a command in the context of a selected package.\n\n```bash\n# Run in current directory\ncondu exec npm run test\n\n# Run in a specific package\ncondu exec --pkg @myorg/my-package npm run test\n```\n\nOptions:\n\n- `--cwd <path>`: Specify the working directory\n- `--pkg <package>`: Specify the target package\n\n### Helper Commands\n\n- `condu help`: Shows help information\n- `condu version`: Shows the current condu version\n\n## Best Practices\n\n1. **Keep features focused**: Each feature should manage one aspect of configuration\n2. **Use peer contexts** for cross-feature coordination, e.g. if you know that the project is TypeScript-based, you might want to enable TS-specific linters in your linter feature\n3. **Use presets** to combine common feature sets, making common boilerplates like `create-react-app` obsolete\n4. **Create custom features** for organization-specific configuration patterns\n5. **Commit the generated files** to source control for transparency\n\n## Preset Example\n\nPresets combine multiple features with sensible defaults:\n\n```typescript\n// monorepo.ts\nexport const monorepo =\n  (options = {}) =>\n  (pkg) => ({\n    projects: [\n      {\n        parentPath: \"packages\",\n        nameConvention: `@${pkg.name}/*`,\n      },\n    ],\n    features: [\n      typescript(options.typescript),\n      eslint(options.eslint),\n      prettier(options.prettier),\n      pnpm(options.pnpm),\n      // Add more features\n    ],\n  });\n```\n\nUse a preset in your project:\n\n```typescript\nimport { configure } from \"condu\";\nimport { monorepo } from \"@condu-preset/monorepo\";\n\nexport default configure(\n  monorepo({\n    // Override specific feature options\n    typescript: {\n      preset: \"commonjs-first\",\n    },\n  }),\n);\n```\n\n## Conclusion\n\ncondu streamlines configuration management by:\n\n- Centralizing all configuration in code\n- Providing strong typing with TypeScript\n- Enabling reuse across projects\n- Minimizing boilerplate\n- Making updates easier to apply\n\nSay goodbye to config hell and focus on building your application!\n","readmeFilename":"README.md"}