Skip to content

[Feature]: Add a --scope flag and automatic scope detection from monorepo workspace package names (Nx, Turborepo, pnpm workspaces) to improve commit message accuracy in multi-package repositories #75

Description

@divyanshim27

Summary

Gitbun's Conventional Commits output includes a scope field (e.g., feat(analyzer): ...). The current scope detection uses the primary modified directory name as the scope. In monorepos using Nx, Turborepo, or pnpm workspaces — where the actual package names differ from directory names — this produces incorrect or misleading scopes like feat(packages): ... instead of feat(ui-components): ....

Problem

  • The current scope detection reads the top-level modified directory (e.g., packages/ui-components/src/Button.tsx → scope: packages).
  • In a monorepo, packages is meaningless as a scope — the correct scope is ui-components (the workspace package name).
  • package.json files at workspace package roots contain the canonical name field (e.g., @myapp/ui-components) — this is never consulted during scope detection.
  • There is no --scope <name> flag to manually override scope detection when the automatic result is wrong.
  • Gitbun is a commit message tool primarily used by developers in complex codebases — monorepos are extremely common in that demographic (Next.js, NestJS, React Native projects, etc.).

Proposed Solution

1. Add a --scope CLI flag:

// src/cli/flags.ts
program
  .option('--scope <scope>', 'Manually set the conventional commit scope')

When --scope is provided, it bypasses all automatic scope detection entirely.

2. Improve automatic scope detection with workspace-aware logic:

// src/analyzer/scopeDetector.ts
import { readFileSync, existsSync } from 'fs';
import { join, dirname } from 'path';

export function detectScope(modifiedFiles: string[]): string {
  // Walk up from the modified file's directory to find the nearest package.json
  for (const file of modifiedFiles) {
    let dir = dirname(file);
    while (dir !== '.' && dir !== '/') {
      const pkgPath = join(dir, 'package.json');
      if (existsSync(pkgPath)) {
        try {
          const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
          if (pkg.name) {
            // Strip org scope prefix: @myapp/ui-components → ui-components
            return pkg.name.replace(/^@[^/]+\//, '');
          }
        } catch {
          // malformed package.json — skip
        }
      }
      dir = dirname(dir);
    }
  }

  // Fallback: use the top-level directory name
  return modifiedFiles[0]?.split('/')[0] ?? 'core';
}

3. Handle pnpm/Turborepo workspace configs:

// Check pnpm-workspace.yaml or turbo.json to understand the monorepo structure
// and validate that the detected scope corresponds to a real workspace package

4. Interactive mode scope selection:
When Gitbun detects multiple workspace packages in the staged diff, the interactive preview will show a scope picker:

? Multiple packages modified. Which scope should this commit use?
❯ ui-components (3 files changed)
api-client (1 file changed)
shared-utils (2 files changed)
[custom scope...]

Additional Notes

  • The workspace-aware detection is entirely filesystem-based — no new dependencies are needed.
  • The --scope flag takes precedence over all automatic detection.
  • Detection falls back gracefully to the existing top-level directory logic if no package.json is found in the file tree.
  • I will add unit tests for scopeDetector.ts covering the monorepo case (nested package.json) and the fallback case.

Could you assign this issue to me?

Labels: enhancement, feature, cli, GSSoC 2026

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions