@eldrforge/kodrdriv 0.0.3 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,20 @@
1
1
  verbose: false
2
- model: gpt-4o
2
+ model: gpt-4.1
3
3
  contextDirectories:
4
- - .kodrdriv/context/people
5
- - .kodrdriv/context/projects
4
+ - .kodrdriv/context
6
5
  commit:
6
+ add: true
7
7
  cached: true
8
8
  sendit: true
9
- messageLimit: 10
10
9
  release:
11
10
  from: main
12
- to: HEAD
11
+ to: HEAD
12
+ publish:
13
+ mergeMethod: squash
14
+ dependencyUpdatePatterns: ["@theunwalked/*", "@riotprompt/*"]
15
+ requiredEnvVars: ["NODE_AUTH_TOKEN"]
16
+ link:
17
+ scopeRoots:
18
+ "@theunwalked": "../../SemicolonAmbulance"
19
+ "@riotprompt": "../../StJustReckoning"
20
+ workspaceFile: "pnpm-workspace.yaml"
@@ -0,0 +1 @@
1
+ KodrDriv is a tool that is designed to help automate the creation of commit messages and release notes. It is also a tool that has been designed to support an opinionated approach to organizing related projects and conducting structured releases with Git. This initial version focuses on pnpm and GitHub, but there are plans to expand coverage over time.
package/README.md CHANGED
@@ -14,7 +14,7 @@ This will make the `kodrdriv` command available globally on your system.
14
14
 
15
15
  ## Commands
16
16
 
17
- KodrDriv provides two main commands:
17
+ KodrDriv provides four main commands:
18
18
 
19
19
  ### Commit Command
20
20
 
@@ -53,6 +53,115 @@ kodrdriv release
53
53
  >
54
54
  > You can use the `--from` and `--to` options to generate release notes comparing two different releases. For example, to see what changed between v1.0.0 and v1.1.0, you could use `kodrdriv release --from v1.0.0 --to v1.1.0`. This is particularly useful for creating detailed changelogs when preparing release documentation.
55
55
 
56
+ ### Publish Command
57
+
58
+ Automate the entire release process, from dependency updates to GitHub release creation:
59
+
60
+ ```bash
61
+ kodrdriv publish
62
+ ```
63
+
64
+ The `publish` command orchestrates a comprehensive release workflow, designed to ensure a safe and consistent release process. Here's what it does:
65
+
66
+ 1. **Dependency Management**: If a `pnpm-workspace.yaml` file is present, it's temporarily renamed to switch from workspace dependencies to registry versions. It then runs `pnpm update --latest` to ensure dependencies are up to date. You can configure specific dependency patterns to update instead of updating all dependencies using the `dependencyUpdatePatterns` configuration option.
67
+
68
+ 2. **Pre-flight Checks**: Before committing any changes, it runs the `prepublishOnly` script from your `package.json`. This script should contain your project's pre-flight checks (e.g., `clean`, `lint`, `build`, `test`) to ensure the project is in a good state. **Note**: A `prepublishOnly` script is required in your `package.json` - the publish command will fail if this script is not present.
69
+
70
+ 3. **Release Commit**: If there are changes to `package.json` or `pnpm-lock.yaml`, it creates an intelligent commit message for the dependency updates.
71
+
72
+ 4. **Version Bump**: It automatically bumps the patch version of your project.
73
+
74
+ 5. **Release Notes**: It generates release notes based on the recent changes and saves them to `RELEASE_NOTES.md`.
75
+
76
+ 6. **Pull Request Automation**:
77
+ * It pushes the changes and tags to the origin.
78
+ * It creates a new pull request for the release.
79
+ * It waits for all status checks on the pull request to pass.
80
+ * Once checks are complete, it automatically merges the pull request using the configured merge method (default: squash).
81
+
82
+ 7. **GitHub Release**: After the PR is merged, it checks out the `main` branch, pulls the latest changes, and creates a new GitHub release with the tag and release notes.
83
+
84
+ 8. **New Release Branch**: Finally, it creates and pushes a new release branch for the next version (e.g., `release/0.0.5`).
85
+
86
+ This command is designed for repositories that follow a pull-request-based release workflow with required status checks. It streamlines the process, reducing manual steps and potential for error.
87
+
88
+ > [!TIP]
89
+ > ### Choosing the Right Merge Method
90
+ >
91
+ > The merge method you choose affects your Git history and can impact your team's workflow:
92
+ >
93
+ > - **Squash** (default): Best for keeping a clean, linear history. All commits from the feature branch are combined into a single commit, making it easier to understand what was changed in each release.
94
+ >
95
+ > - **Merge**: Preserves the complete commit history from the feature branch. Use this when you want to maintain detailed development history and individual commit information.
96
+ >
97
+ > - **Rebase**: Creates a linear history without merge commits. The commits from the feature branch are replayed on top of the target branch. This keeps history clean while preserving individual commits.
98
+ >
99
+ > Consider your team's preferences and any branch protection rules when choosing a merge method.
100
+
101
+ ### Link Command
102
+
103
+ Manage pnpm workspace links for local development with sibling projects:
104
+
105
+ ```bash
106
+ kodrdriv link
107
+ ```
108
+
109
+ The `link` command automates the creation and management of pnpm workspace configurations for local development. It scans your project's dependencies and automatically discovers matching sibling packages in configured scope directories, then updates your `pnpm-workspace.yaml` file to link them for local development.
110
+
111
+ This is particularly useful when working with monorepos or related packages where you want to use local versions of dependencies instead of published registry versions during development.
112
+
113
+ **How it works:**
114
+
115
+ 1. **Dependency Analysis**: Reads your `package.json` and examines all dependencies (including devDependencies and peerDependencies)
116
+
117
+ 2. **Scope-based Scanning**: For each configured scope (e.g., `@company`, `@myorg`), it scans the specified root directory to find all available packages by reading their `package.json` files
118
+
119
+ 3. **Smart Matching**: Instead of relying on directory naming conventions, it matches dependencies by their actual package names from `package.json`, handling cases where directory names don't match package names
120
+
121
+ 4. **Workspace Management**: Updates or creates a `pnpm-workspace.yaml` file with the discovered local packages, preserving any existing workspace configuration
122
+
123
+ **Example Scenario:**
124
+
125
+ Suppose you're working on `@company/api` and it depends on `@company/logging`. Your directory structure might look like:
126
+
127
+ ```
128
+ workspace/
129
+ ├── company-api/ # Contains @company/api
130
+ ├── company-logging-lib/ # Contains @company/logging (note: different directory name)
131
+ └── other-project/
132
+ ```
133
+
134
+ The link command would:
135
+ - Scan `../` for packages with scope `@company`
136
+ - Find `@company/logging` in `../company-logging-lib/`
137
+ - Update `pnpm-workspace.yaml` to include `../company-logging-lib`
138
+
139
+ **Configuration Requirements:**
140
+
141
+ The link command requires scope root mappings to be configured. You can provide these via:
142
+
143
+ 1. **Command line**: `--scope-roots '{"@company": "../", "@myorg": "../../"}'`
144
+ 2. **Configuration file**: Set `link.scopeRoots` in your `.kodrdriv/config.json`
145
+
146
+ **Dry Run Support:**
147
+
148
+ Use `--dry-run` to preview what packages would be linked without making changes:
149
+
150
+ ```bash
151
+ kodrdriv link --scope-roots '{"@company": "../"}' --dry-run
152
+ ```
153
+
154
+ > [!TIP]
155
+ > ### Best Practices for Link Command
156
+ >
157
+ > - **Use relative paths**: Configure scope roots with relative paths (like `../` or `../../`) to ensure the workspace file works across different environments
158
+ >
159
+ > - **Scope organization**: Group related packages under the same scope (e.g., `@company/api`, `@company/logging`) for easier management
160
+ >
161
+ > - **Version with caution**: Consider whether to commit the generated `pnpm-workspace.yaml` file. You might want to add it to `.gitignore` if it's only for local development
162
+ >
163
+ > - **Multiple scopes**: You can configure multiple scope roots to handle packages from different organizations or teams in the same workspace
164
+
56
165
  ## Command Line Options
57
166
 
58
167
  KodrDriv provides several command line options to customize its behavior:
@@ -71,6 +180,180 @@ KodrDriv provides several command line options to customize its behavior:
71
180
  - `--context <context>`: Provide additional context (as a string or file path) to guide the commit message generation. This context is included in the prompt sent to the AI and can be used to specify the purpose, theme, or any special considerations for the commit.
72
181
  - `--message-limit <messageLimit>`: Limit the number of recent commit messages (from git log) to include in the prompt for context (default: 10). This can help focus the AI on the most relevant recent changes.
73
182
 
183
+ ### Publish Command Options
184
+
185
+ - `--merge-method <method>`: Method to merge pull requests during the publish process (default: 'squash')
186
+ - Available methods: 'merge', 'squash', 'rebase'
187
+ - `merge`: Creates a merge commit that combines the feature branch into the target branch
188
+ - `squash`: Combines all commits from the feature branch into a single commit on the target branch
189
+ - `rebase`: Replays commits from the feature branch onto the target branch without creating a merge commit
190
+
191
+ #### Dependency Update Configuration
192
+
193
+ The publish command supports selective dependency updates through the `dependencyUpdatePatterns` configuration option. This allows you to specify which dependencies should be updated during the release process instead of updating all dependencies.
194
+
195
+ **Configuration:**
196
+ ```json
197
+ {
198
+ "publish": {
199
+ "dependencyUpdatePatterns": ["@company/*", "@myorg/*", "specific-package"]
200
+ }
201
+ }
202
+ ```
203
+
204
+ **Behavior:**
205
+ - When patterns are specified, only dependencies matching those patterns will be updated
206
+ - Patterns can include npm scopes (e.g., `@company/*`) or specific package names
207
+ - If no patterns are configured, all dependencies are updated (default behavior)
208
+ - This is particularly useful when developing a set of related packages where you want to ensure you're using the latest versions of your organization's packages while keeping other dependencies stable
209
+
210
+ #### Environment Variable Configuration
211
+
212
+ The publish command includes comprehensive environment variable validation to ensure all required credentials and tokens are available before starting the release process. This prevents failures partway through the publication workflow.
213
+
214
+ **Core Required Environment Variables:**
215
+
216
+ The following environment variables are required by default for all publish operations:
217
+
218
+ - `GITHUB_TOKEN`: Required for GitHub API operations (creating pull requests, releases, etc.)
219
+ - `OPENAI_API_KEY`: Required for AI-powered commit message and release note generation
220
+
221
+ **Configurable Additional Environment Variables:**
222
+
223
+ You can specify additional required environment variables specific to your project or organization:
224
+
225
+ **Configuration (config.yaml):**
226
+ ```yaml
227
+ publish:
228
+ requiredEnvVars:
229
+ - NODE_AUTH_TOKEN # Often needed for publishing to npm
230
+ - DEPLOY_KEY # Custom deployment credentials
231
+ - CUSTOM_API_TOKEN # Organization-specific tokens
232
+ - CODECOV_TOKEN # Code coverage reporting
233
+ ```
234
+
235
+ **Configuration (config.json):**
236
+ ```json
237
+ {
238
+ "publish": {
239
+ "requiredEnvVars": [
240
+ "NODE_AUTH_TOKEN",
241
+ "DEPLOY_KEY",
242
+ "CUSTOM_API_TOKEN",
243
+ "CODECOV_TOKEN"
244
+ ]
245
+ }
246
+ }
247
+ ```
248
+
249
+ **Automatic .npmrc Scanning:**
250
+
251
+ The publish command automatically scans your `.npmrc` file for environment variable references and includes them in the validation. It detects common patterns like:
252
+
253
+ - `${NODE_AUTH_TOKEN}` - Curly brace syntax
254
+ - `$NODE_AUTH_TOKEN` - Direct variable reference
255
+
256
+ Example `.npmrc` file:
257
+ ```
258
+ @myorg:registry=https://npm.pkg.github.com/
259
+ //npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
260
+ ```
261
+
262
+ In this case, `NODE_AUTH_TOKEN` would be automatically detected and validated.
263
+
264
+ **Validation Behavior:**
265
+ - All environment variable checks happen during the initial prechecks phase
266
+ - If any required environment variables are missing, the publish command fails immediately with a clear error message
267
+ - The error message lists all missing variables at once, making it easy to set them all before retrying
268
+ - Variables from configuration, .npmrc scanning, and core requirements are combined and deduplicated
269
+
270
+ **Example Error:**
271
+ ```
272
+ Missing required environment variables: NODE_AUTH_TOKEN, DEPLOY_KEY.
273
+ Please set these environment variables before running publish.
274
+ ```
275
+
276
+ **Security Best Practices:**
277
+ - Store sensitive environment variables in your CI/CD system's secure variable storage
278
+ - Use tools like 1Password CLI, HashiCorp Vault, or similar for local development
279
+ - Never commit environment variables or tokens to your repository
280
+ - Consider using `.env` files (with proper .gitignore configuration) for local development
281
+
282
+ #### prepublishOnly Script Requirement
283
+
284
+ The publish command requires a `prepublishOnly` script to be defined in your `package.json`. This script should contain all the checks and builds necessary to verify your project is ready for release.
285
+
286
+ **Example package.json:**
287
+ ```json
288
+ {
289
+ "scripts": {
290
+ "prepublishOnly": "pnpm run clean && pnpm run lint && pnpm run build && pnpm run test",
291
+ "clean": "rimraf dist",
292
+ "lint": "eslint src/",
293
+ "build": "tsc",
294
+ "test": "vitest run"
295
+ }
296
+ }
297
+ ```
298
+
299
+ **Why prepublishOnly?**
300
+ - The `prepublishOnly` script is an npm/pnpm standard that runs automatically before publishing packages
301
+ - It ensures consistent pre-flight checks regardless of how the package is published
302
+ - It allows projects to define their own specific validation pipeline
303
+ - The publish command validates this script exists during the initial prechecks phase
304
+
305
+ **Common prepublishOnly Patterns:**
306
+ - `"prepublishOnly": "npm run test"` - Run tests only
307
+ - `"prepublishOnly": "npm run build && npm run test"` - Build and test
308
+ - `"prepublishOnly": "npm run lint && npm run build && npm run test"` - Full validation pipeline
309
+ - `"prepublishOnly": "npm run ci"` - Delegate to a CI script that includes all checks
310
+
311
+ If this script is missing, the publish command will fail immediately with a helpful error message explaining the requirement.
312
+
313
+ ### Link Command Options
314
+
315
+ - `--scope-roots <scopeRoots>`: JSON mapping of scopes to root directories for package discovery
316
+ - **Format**: `'{"@scope": "path", "@another": "path"}'`
317
+ - **Example**: `'{"@company": "../", "@myorg": "../../packages/"}'`
318
+ - **Required**: At least one scope mapping must be provided
319
+ - `--workspace-file <workspaceFile>`: Path to the workspace file to create/update (default: 'pnpm-workspace.yaml')
320
+ - Can specify custom workspace file names or paths
321
+ - Useful if your project uses non-standard workspace file naming
322
+
323
+ #### Scope Root Configuration
324
+
325
+ The `--scope-roots` option accepts a JSON object mapping package scopes to their corresponding root directories. Each entry tells the link command where to look for packages with that scope.
326
+
327
+ **Configuration Examples:**
328
+
329
+ ```bash
330
+ # Single scope
331
+ kodrdriv link --scope-roots '{"@company": "../"}'
332
+
333
+ # Multiple scopes with different paths
334
+ kodrdriv link --scope-roots '{"@company": "../", "@tools": "../../tools/", "@shared": "../shared-packages/"}'
335
+
336
+ # Absolute paths (not recommended for portability)
337
+ kodrdriv link --scope-roots '{"@company": "/Users/dev/projects/company-packages/"}'
338
+ ```
339
+
340
+ **Path Resolution:**
341
+ - Relative paths are resolved from the current working directory
342
+ - Use `../` to scan the parent directory
343
+ - Use `../../` to scan two levels up
344
+ - Multiple levels and subdirectories are supported: `../../company/packages/`
345
+
346
+ **Package Discovery Process:**
347
+ 1. For each scope root, the command lists all subdirectories
348
+ 2. It reads each subdirectory's `package.json` to get the actual package name
349
+ 3. It matches discovered package names against your project's dependencies
350
+ 4. Matching packages are added to the workspace configuration
351
+
352
+ This approach handles complex scenarios where:
353
+ - Directory names don't match package names (e.g., `logging-lib` directory contains `@company/logging`)
354
+ - Packages are organized in different directory structures
355
+ - Multiple related packages exist in the same scope root
356
+
74
357
  ### OpenAI Configuration
75
358
 
76
359
  - `--openai-api-key <key>`: OpenAI API key (can also be set via OPENAI_API_KEY environment variable)
@@ -131,6 +414,36 @@ kodrdriv commit --context "Refactoring for performance" --message-limit 5
131
414
  kodrdriv release --context "Quarterly release, focus on stability" --message-limit 20
132
415
  ```
133
416
 
417
+ Publish with different merge methods:
418
+ ```bash
419
+ # Use default squash merge
420
+ kodrdriv publish
421
+
422
+ # Use merge commit (preserves individual commits)
423
+ kodrdriv publish --merge-method merge
424
+
425
+ # Use rebase (clean linear history)
426
+ kodrdriv publish --merge-method rebase
427
+ ```
428
+
429
+ Link local packages for development:
430
+ ```bash
431
+ # Basic linking with single scope
432
+ kodrdriv link --scope-roots '{"@company": "../"}'
433
+
434
+ # Multiple scopes with different root directories
435
+ kodrdriv link --scope-roots '{"@company": "../", "@tools": "../../tools/"}'
436
+
437
+ # Dry run to preview changes
438
+ kodrdriv link --scope-roots '{"@company": "../"}' --dry-run --verbose
439
+
440
+ # Custom workspace file
441
+ kodrdriv link --scope-roots '{"@company": "../"}' --workspace-file "workspace.yaml"
442
+
443
+ # Real-world example: linking @company packages from company directory
444
+ kodrdriv link --scope-roots '{"@company": "../../company/"}'
445
+ ```
446
+
134
447
  ### Configuration Directory
135
448
 
136
449
  KodrDriv uses a configuration directory to store custom settings, instructions, and other configuration files. You can specify a custom location using the `--config-dir` option:
@@ -152,9 +465,56 @@ The configuration directory structure is as follows:
152
465
  │ ├── release.md # Override for release instructions
153
466
  │ ├── release-pre.md # Content prepended to default release instructions
154
467
  │ └── release-post.md # Content appended to default release instructions
468
+ ├── config.json # Main configuration file
155
469
  └── ... # Other configuration files
156
470
  ```
157
471
 
472
+ ### Configuration File
473
+
474
+ You can create a `config.json` file in your `.kodrdriv` directory to set default options for all commands. This allows you to avoid repeating command-line options and ensures consistent behavior across your project.
475
+
476
+ Example configuration file (`.kodrdriv/config.json`):
477
+
478
+ ```json
479
+ {
480
+ "model": "gpt-4o-mini",
481
+ "verbose": true,
482
+ "contextDirectories": ["src", "docs"],
483
+ "publish": {
484
+ "mergeMethod": "merge",
485
+ "dependencyUpdatePatterns": ["@company/*", "@myorg/*"],
486
+ "requiredEnvVars": ["NODE_AUTH_TOKEN", "CUSTOM_TOKEN"]
487
+ },
488
+ "commit": {
489
+ "add": true,
490
+ "messageLimit": 5
491
+ },
492
+ "release": {
493
+ "from": "main",
494
+ "to": "HEAD",
495
+ "messageLimit": 10
496
+ },
497
+ "link": {
498
+ "scopeRoots": {
499
+ "@company": "../",
500
+ "@myorg": "../../org-packages/",
501
+ "@tools": "../shared-tools/"
502
+ },
503
+ "workspaceFile": "pnpm-workspace.yaml"
504
+ },
505
+ "excludedPatterns": [
506
+ "node_modules",
507
+ "dist",
508
+ "*.log"
509
+ ]
510
+ }
511
+ ```
512
+
513
+ Configuration options set in the file can be overridden by command-line arguments. The precedence order is:
514
+ 1. Command-line arguments (highest priority)
515
+ 2. Configuration file
516
+ 3. Default values (lowest priority)
517
+
158
518
  ## Default Instructions
159
519
 
160
520
  KodrDriv comes with default instructions that guide the AI in generating release notes or change logs. These instructions are defined in the source code:
package/dist/arguments.js CHANGED
@@ -16,12 +16,20 @@ z.object({
16
16
  instructions: z.string().optional(),
17
17
  configDir: z.string().optional(),
18
18
  cached: z.boolean().optional(),
19
+ add: z.boolean().optional(),
19
20
  sendit: z.boolean().optional(),
20
21
  from: z.string().optional(),
21
22
  to: z.string().optional(),
22
23
  excludedPatterns: z.array(z.string()).optional(),
23
24
  context: z.string().optional(),
24
- messageLimit: z.number().optional()
25
+ messageLimit: z.number().optional(),
26
+ mergeMethod: z.enum([
27
+ 'merge',
28
+ 'squash',
29
+ 'rebase'
30
+ ]).optional(),
31
+ scopeRoots: z.string().optional(),
32
+ workspaceFile: z.string().optional()
25
33
  });
26
34
  // Function to transform flat CLI args into nested Config structure
27
35
  const transformCliArgs = (finalCliArgs)=>{
@@ -37,8 +45,9 @@ const transformCliArgs = (finalCliArgs)=>{
37
45
  // Map configDir (CLI) to configDirectory (Cardigantime standard)
38
46
  if (finalCliArgs.configDir !== undefined) transformedCliArgs.configDirectory = finalCliArgs.configDir;
39
47
  // Nested mappings for 'commit' options
40
- if (finalCliArgs.cached !== undefined || finalCliArgs.sendit !== undefined) {
48
+ if (finalCliArgs.cached !== undefined || finalCliArgs.sendit !== undefined || finalCliArgs.add !== undefined) {
41
49
  transformedCliArgs.commit = {};
50
+ if (finalCliArgs.add !== undefined) transformedCliArgs.commit.add = finalCliArgs.add;
42
51
  if (finalCliArgs.cached !== undefined) transformedCliArgs.commit.cached = finalCliArgs.cached;
43
52
  if (finalCliArgs.sendit !== undefined) transformedCliArgs.commit.sendit = finalCliArgs.sendit;
44
53
  if (finalCliArgs.messageLimit !== undefined) transformedCliArgs.commit.messageLimit = finalCliArgs.messageLimit;
@@ -52,6 +61,24 @@ const transformCliArgs = (finalCliArgs)=>{
52
61
  if (finalCliArgs.context !== undefined) transformedCliArgs.release.context = finalCliArgs.context;
53
62
  if (finalCliArgs.messageLimit !== undefined) transformedCliArgs.release.messageLimit = finalCliArgs.messageLimit;
54
63
  }
64
+ // Nested mappings for 'publish' options
65
+ if (finalCliArgs.mergeMethod !== undefined) {
66
+ transformedCliArgs.publish = {};
67
+ if (finalCliArgs.mergeMethod !== undefined) transformedCliArgs.publish.mergeMethod = finalCliArgs.mergeMethod;
68
+ }
69
+ // Nested mappings for 'link' and 'unlink' options (both use the same configuration)
70
+ if (finalCliArgs.scopeRoots !== undefined || finalCliArgs.workspaceFile !== undefined) {
71
+ transformedCliArgs.link = {};
72
+ if (finalCliArgs.scopeRoots !== undefined) {
73
+ try {
74
+ transformedCliArgs.link.scopeRoots = JSON.parse(finalCliArgs.scopeRoots);
75
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
76
+ } catch (error) {
77
+ throw new Error(`Invalid JSON for scope-roots: ${finalCliArgs.scopeRoots}`);
78
+ }
79
+ }
80
+ if (finalCliArgs.workspaceFile !== undefined) transformedCliArgs.link.workspaceFile = finalCliArgs.workspaceFile;
81
+ }
55
82
  if (finalCliArgs.excludedPatterns !== undefined) transformedCliArgs.excludedPatterns = finalCliArgs.excludedPatterns;
56
83
  // Note: finalCliArgs.openaiApiKey is intentionally omitted here as it belongs to SecureConfig
57
84
  return transformedCliArgs;
@@ -71,14 +98,38 @@ const configure = async (cardigantime)=>{
71
98
  const transformedCliArgs = transformCliArgs(finalCliArgs);
72
99
  logger.debug('Transformed CLI Args for merging: %s', JSON.stringify(transformedCliArgs, null, 2));
73
100
  // Get values from config file
74
- const fileValues = await cardigantime.read(finalCliArgs);
75
- await cardigantime.validate(fileValues); // Validate file config against the shape
101
+ // Temporary workaround: Read config file manually due to cardigantime parsing issue
102
+ let fileValues = {};
103
+ // Force manual config reading for now
104
+ const configPath = path.join(process.cwd(), '.kodrdriv', 'config.yaml');
105
+ const storage = create({
106
+ log: logger.info
107
+ });
108
+ const exists = await storage.exists(configPath);
109
+ if (exists) {
110
+ const yaml = await import('js-yaml');
111
+ const configContent = await storage.readFile(configPath, 'utf-8');
112
+ fileValues = yaml.load(configContent);
113
+ // Add the configDirectory since it's not in the config file but is required
114
+ if (!fileValues.configDirectory) {
115
+ fileValues.configDirectory = '.kodrdriv';
116
+ }
117
+ }
118
+ // Temporarily skip cardigantime validation due to parsing issues
119
+ // await cardigantime.validate(fileValues);
76
120
  // Merge configurations: Defaults -> File -> CLI
121
+ // Properly merge the link section to preserve scope roots from config file
122
+ const mergedLink = {
123
+ ...KODRDRIV_DEFAULTS.link,
124
+ ...fileValues.link,
125
+ ...transformedCliArgs.link
126
+ };
77
127
  const partialConfig = {
78
128
  ...KODRDRIV_DEFAULTS,
79
129
  ...fileValues,
80
- ...transformedCliArgs
81
- }; // Cast to Partial<MainConfig> initially
130
+ ...transformedCliArgs,
131
+ link: mergedLink
132
+ }; // Cast to Partial<Config> initially
82
133
  // Specific validation and processing after merge
83
134
  const config = await validateAndProcessOptions(partialConfig);
84
135
  logger.verbose('Final configuration: %s', JSON.stringify(config, null, 2));
@@ -97,24 +148,39 @@ function getCliConfig(program) {
97
148
  .option('--excluded-paths [excludedPatterns...]', 'paths to exclude from the diff');
98
149
  };
99
150
  // Add subcommands
100
- const commitCommand = program.command('commit').option('--cached', 'use cached diff').option('--sendit', 'Commit with the message generated. No review.').option('--context <context>', 'context for the commit message').option('--message-limit <messageLimit>', 'limit the number of messages to generate').description('Generate commit notes');
151
+ const commitCommand = program.command('commit').option('--cached', 'use cached diff').option('--add', 'add all changes before committing').option('--sendit', 'Commit with the message generated. No review.').option('--context <context>', 'context for the commit message').option('--message-limit <messageLimit>', 'limit the number of messages to generate').description('Generate commit notes');
101
152
  addSharedOptions(commitCommand);
102
153
  const releaseCommand = program.command('release').option('--from <from>', 'branch to generate release notes from').option('--to <to>', 'branch to generate release notes to').option('--context <context>', 'context for the commit message').description('Generate release notes');
103
154
  addSharedOptions(releaseCommand);
155
+ const publishCommand = program.command('publish').option('--merge-method <method>', 'method to merge PR (merge, squash, rebase)', 'squash').description('Publish a release');
156
+ addSharedOptions(publishCommand);
157
+ const linkCommand = program.command('link').option('--scope-roots <scopeRoots>', 'JSON mapping of scopes to root directories (e.g., \'{"@company": "../"}\')').option('--workspace-file <workspaceFile>', 'path to workspace file', 'pnpm-workspace.yaml').description('Manage pnpm workspace links for local development');
158
+ addSharedOptions(linkCommand);
159
+ const unlinkCommand = program.command('unlink').option('--scope-roots <scopeRoots>', 'JSON mapping of scopes to root directories (e.g., \'{"@company": "../"}\')').option('--workspace-file <workspaceFile>', 'path to workspace file', 'pnpm-workspace.yaml').description('Remove pnpm workspace links and rebuild dependencies');
160
+ addSharedOptions(unlinkCommand);
104
161
  program.parse();
105
162
  const cliArgs = program.opts(); // Get all opts initially
106
163
  // Determine which command is being run
107
164
  let commandName = DEFAULT_COMMAND;
108
165
  let commandOptions = {}; // Store specific command options
109
- if (program.args.length > 0 && ALLOWED_COMMANDS.includes(program.args[0])) {
166
+ if (program.args.length > 0) {
110
167
  commandName = program.args[0];
168
+ }
169
+ validateCommand(commandName);
170
+ // Only proceed with command-specific options if validation passed
171
+ if (ALLOWED_COMMANDS.includes(commandName)) {
111
172
  if (commandName === 'commit' && commitCommand.opts) {
112
173
  commandOptions = commitCommand.opts();
113
174
  } else if (commandName === 'release' && releaseCommand.opts) {
114
175
  commandOptions = releaseCommand.opts();
176
+ } else if (commandName === 'publish' && publishCommand.opts) {
177
+ commandOptions = publishCommand.opts();
178
+ } else if (commandName === 'link' && linkCommand.opts) {
179
+ commandOptions = linkCommand.opts();
180
+ } else if (commandName === 'unlink' && unlinkCommand.opts) {
181
+ commandOptions = unlinkCommand.opts();
115
182
  }
116
183
  }
117
- validateCommand(commandName);
118
184
  // Include command name in CLI args for merging
119
185
  const finalCliArgs = {
120
186
  ...cliArgs,
@@ -141,13 +207,13 @@ async function validateAndProcessSecureOptions() {
141
207
  }
142
208
  // Renamed validation function to reflect its broader role
143
209
  async function validateAndProcessOptions(options) {
144
- var _options_commit, _options_commit1, _options_commit2, _options_commit3, _options_release, _options_release1, _options_release2, _options_release3;
210
+ var _options_commit, _options_commit1, _options_commit2, _options_commit3, _options_commit4, _options_release, _options_release1, _options_release2, _options_release3, _options_publish, _options_publish1, _options_publish2, _options_link, _options_link1, _options_link2;
145
211
  const contextDirectories = await validateContextDirectories(options.contextDirectories || KODRDRIV_DEFAULTS.contextDirectories);
146
212
  const instructionsPathOrContent = options.instructions || KODRDRIV_DEFAULTS.instructions;
147
213
  const instructions = await validateAndReadInstructions(instructionsPathOrContent);
148
214
  const configDir = options.configDirectory || KODRDRIV_DEFAULTS.configDirectory;
149
215
  await validateConfigDir(configDir); // Keep validation, but maybe remove return if not used elsewhere
150
- var _options_dryRun, _options_verbose, _options_debug, _options_overrides, _options_model, _options_commit_cached, _options_commit_sendit, _options_commit_messageLimit, _options_release_from, _options_release_to, _options_release_messageLimit, _options_excludedPatterns;
216
+ var _options_dryRun, _options_verbose, _options_debug, _options_overrides, _options_model, _options_commit_add, _options_commit_cached, _options_commit_sendit, _options_commit_messageLimit, _options_release_from, _options_release_to, _options_release_messageLimit, _options_publish_mergeMethod, _options_publish_requiredEnvVars, _options_link_scopeRoots, _options_link_workspaceFile, _options_link_dryRun, _options_excludedPatterns;
151
217
  // Ensure all required fields are present and have correct types after merging
152
218
  const finalConfig = {
153
219
  dryRun: (_options_dryRun = options.dryRun) !== null && _options_dryRun !== void 0 ? _options_dryRun : KODRDRIV_DEFAULTS.dryRun,
@@ -160,10 +226,11 @@ async function validateAndProcessOptions(options) {
160
226
  configDirectory: configDir,
161
227
  // Command-specific options with defaults
162
228
  commit: {
163
- cached: (_options_commit_cached = (_options_commit = options.commit) === null || _options_commit === void 0 ? void 0 : _options_commit.cached) !== null && _options_commit_cached !== void 0 ? _options_commit_cached : KODRDRIV_DEFAULTS.commit.cached,
164
- sendit: (_options_commit_sendit = (_options_commit1 = options.commit) === null || _options_commit1 === void 0 ? void 0 : _options_commit1.sendit) !== null && _options_commit_sendit !== void 0 ? _options_commit_sendit : KODRDRIV_DEFAULTS.commit.sendit,
165
- messageLimit: (_options_commit_messageLimit = (_options_commit2 = options.commit) === null || _options_commit2 === void 0 ? void 0 : _options_commit2.messageLimit) !== null && _options_commit_messageLimit !== void 0 ? _options_commit_messageLimit : KODRDRIV_DEFAULTS.commit.messageLimit,
166
- context: (_options_commit3 = options.commit) === null || _options_commit3 === void 0 ? void 0 : _options_commit3.context
229
+ add: (_options_commit_add = (_options_commit = options.commit) === null || _options_commit === void 0 ? void 0 : _options_commit.add) !== null && _options_commit_add !== void 0 ? _options_commit_add : KODRDRIV_DEFAULTS.commit.add,
230
+ cached: (_options_commit_cached = (_options_commit1 = options.commit) === null || _options_commit1 === void 0 ? void 0 : _options_commit1.cached) !== null && _options_commit_cached !== void 0 ? _options_commit_cached : KODRDRIV_DEFAULTS.commit.cached,
231
+ sendit: (_options_commit_sendit = (_options_commit2 = options.commit) === null || _options_commit2 === void 0 ? void 0 : _options_commit2.sendit) !== null && _options_commit_sendit !== void 0 ? _options_commit_sendit : KODRDRIV_DEFAULTS.commit.sendit,
232
+ messageLimit: (_options_commit_messageLimit = (_options_commit3 = options.commit) === null || _options_commit3 === void 0 ? void 0 : _options_commit3.messageLimit) !== null && _options_commit_messageLimit !== void 0 ? _options_commit_messageLimit : KODRDRIV_DEFAULTS.commit.messageLimit,
233
+ context: (_options_commit4 = options.commit) === null || _options_commit4 === void 0 ? void 0 : _options_commit4.context
167
234
  },
168
235
  release: {
169
236
  from: (_options_release_from = (_options_release = options.release) === null || _options_release === void 0 ? void 0 : _options_release.from) !== null && _options_release_from !== void 0 ? _options_release_from : KODRDRIV_DEFAULTS.release.from,
@@ -171,6 +238,16 @@ async function validateAndProcessOptions(options) {
171
238
  messageLimit: (_options_release_messageLimit = (_options_release2 = options.release) === null || _options_release2 === void 0 ? void 0 : _options_release2.messageLimit) !== null && _options_release_messageLimit !== void 0 ? _options_release_messageLimit : KODRDRIV_DEFAULTS.release.messageLimit,
172
239
  context: (_options_release3 = options.release) === null || _options_release3 === void 0 ? void 0 : _options_release3.context
173
240
  },
241
+ publish: {
242
+ mergeMethod: (_options_publish_mergeMethod = (_options_publish = options.publish) === null || _options_publish === void 0 ? void 0 : _options_publish.mergeMethod) !== null && _options_publish_mergeMethod !== void 0 ? _options_publish_mergeMethod : KODRDRIV_DEFAULTS.publish.mergeMethod,
243
+ dependencyUpdatePatterns: (_options_publish1 = options.publish) === null || _options_publish1 === void 0 ? void 0 : _options_publish1.dependencyUpdatePatterns,
244
+ requiredEnvVars: (_options_publish_requiredEnvVars = (_options_publish2 = options.publish) === null || _options_publish2 === void 0 ? void 0 : _options_publish2.requiredEnvVars) !== null && _options_publish_requiredEnvVars !== void 0 ? _options_publish_requiredEnvVars : KODRDRIV_DEFAULTS.publish.requiredEnvVars
245
+ },
246
+ link: {
247
+ scopeRoots: (_options_link_scopeRoots = (_options_link = options.link) === null || _options_link === void 0 ? void 0 : _options_link.scopeRoots) !== null && _options_link_scopeRoots !== void 0 ? _options_link_scopeRoots : KODRDRIV_DEFAULTS.link.scopeRoots,
248
+ workspaceFile: (_options_link_workspaceFile = (_options_link1 = options.link) === null || _options_link1 === void 0 ? void 0 : _options_link1.workspaceFile) !== null && _options_link_workspaceFile !== void 0 ? _options_link_workspaceFile : KODRDRIV_DEFAULTS.link.workspaceFile,
249
+ dryRun: (_options_link_dryRun = (_options_link2 = options.link) === null || _options_link2 === void 0 ? void 0 : _options_link2.dryRun) !== null && _options_link_dryRun !== void 0 ? _options_link_dryRun : KODRDRIV_DEFAULTS.link.dryRun
250
+ },
174
251
  excludedPatterns: (_options_excludedPatterns = options.excludedPatterns) !== null && _options_excludedPatterns !== void 0 ? _options_excludedPatterns : KODRDRIV_DEFAULTS.excludedPatterns
175
252
  };
176
253
  // Final validation against the MainConfig shape (optional, cardigantime might handle it)
@@ -258,5 +335,5 @@ async function validateAndReadInstructions(instructionsPath) {
258
335
  }
259
336
  }
260
337
 
261
- export { configure, transformCliArgs, validateAndReadInstructions, validateCommand, validateContextDirectories };
338
+ export { configure, getCliConfig, transformCliArgs, validateAndProcessOptions, validateAndProcessSecureOptions, validateAndReadInstructions, validateCommand, validateConfigDir, validateContextDirectories };
262
339
  //# sourceMappingURL=arguments.js.map