npm package: @garrick0/c2-observable-git
Description: C2 Git Observable - Git repository observer and checks
Author: garrick0
Latest version: 6.1.0

# @garrick0/c2-observable-git

> Git Observable Plugin for the C2 system. Provides git repository observation and checks.

## Installation

```bash
npm install @garrick0/c2-observable-git
```

## Usage

### With the C2 CLI

The CLI includes this plugin by default. Just create a config file:

```typescript
// c2.config.ts
import { defineUserConfig } from '@garrick0/c2';

export default defineUserConfig({
  extends: ['c2:recommended'],  // or 'git:recommended'
  checks: {
    'git:no-protected-branch': 'error',
  },
});
```

### Programmatic Usage

```typescript
import { run, defineUserConfig, createContextFromPlugins } from '@garrick0/c2';
import { createGitPlugin } from '@garrick0/c2-observable-git';

// User config (WHAT to check)
const userConfig = defineUserConfig({
  extends: ['c2:recommended'],
});

// Host context (HOW to run)
const context = createContextFromPlugins([
  createGitPlugin({
    protectedBranches: ['main', 'master', 'develop'],
    recentCommitCount: 10,
  }),
]);

// Execute checks
const result = await run({ userConfig, context });
console.log(`${result.summary.passed}/${result.summary.total} checks passed`);
```

## Check Definitions (13 total)

### Branching
| Check ID | Description | Default Severity |
|----------|-------------|------------------|
| `git:no-protected-branch` | Prevents working on protected branches | error |
| `git:branch-naming` | Enforces branch naming conventions | warning |
| `git:not-detached-head` | Warns about detached HEAD state | warning |

### Hygiene
| Check ID | Description | Default Severity |
|----------|-------------|------------------|
| `git:uncommitted-lines-limit` | Warns about too many uncommitted lines | warning |
| `git:clean-working-tree` | Warns about uncommitted changes | warning |
| `git:no-untracked-files` | Warns about untracked files | warning |

### Sync
| Check ID | Description | Default Severity |
|----------|-------------|------------------|
| `git:up-to-date-with-remote` | Warns when out of sync with remote | warning |

### History
| Check ID | Description | Default Severity |
|----------|-------------|------------------|
| `git:no-merge-commits` | Encourages linear history | warning |
| `git:commit-message-format` | Enforces conventional commit format | warning |
| `git:no-wip-commits` | Warns about WIP commits | warning |
| `git:no-lock-file-only-commits` | Warns about lock-file-only commits | info |

### Workflow
| Check ID | Description | Default Severity |
|----------|-------------|------------------|
| `git:has-linear-issue` | Ensures Linear issue reference | warning |
| `git:has-valid-author` | Ensures git author is configured | error |

## Profiles

This plugin provides several built-in profiles:

### git:recommended
Sensible defaults for most projects:
- Block working on protected branches (error)
- Warn about uncommitted changes
- Warn about WIP commits

### git:strict
All checks enabled with error severity for CI/CD enforcement.

### git:minimal
Only critical checks for permissive development:
- Block protected branches
- Ensure valid author

### git:ci
Checks suited for CI environments:
- No WIP commits (error)
- Commit message format (error)
- No merge commits (warning)

## Query Operations (14 total)

| Operation | Description |
|-----------|-------------|
| `branchIsProtected` | Check if current branch is protected |
| `branchMatchesPattern` | Check if branch matches naming pattern |
| `hasUncommittedChanges` | Check for uncommitted changes |
| `uncommittedLinesBelow` | Check if uncommitted lines below threshold |
| `isClean` | Check if working tree is clean |
| `isUpToDateWithRemote` | Check if branch is up to date with remote |
| `hasNoUntrackedFiles` | Check for untracked files |
| `hasNoMergeCommits` | Check for merge commits in history |
| `commitMessagesMatch` | Check if commit messages match pattern |
| `noWipCommits` | Check for WIP commits |
| `hasLinearIssue` | Check for Linear issue references |
| `noLockFileOnlyCommits` | Check for lock-file-only changes |
| `hasValidAuthor` | Check if git user is configured |
| `notDetachedHead` | Check if not in detached HEAD state |

## Plugin Configuration

```typescript
createGitPlugin({
  // Custom git port adapter (for testing)
  gitPort?: IGitPort;

  // Default protected branches (default: ['main', 'master'])
  protectedBranches?: string[];

  // Number of recent commits to retrieve (default: 10)
  recentCommitCount?: number;
});
```

## Architecture

This package uses the **State + Port** pattern for efficient git operations:

- **GitState**: Immutable snapshot of git repository state captured at observation time
- **IGitPort**: Interface for lazy loading additional data when queries need it
- **Query Memoization**: Caches query results to avoid redundant fetches

```
observable-git/
├── domain/
│   ├── GitState.ts         # State type (v2 architecture)
│   ├── GitData.ts          # Data types (CommitInfo)
│   ├── GitObservable.ts    # Observable with async queries
│   ├── matchPattern.ts     # Pattern matching utilities
│   └── queries/            # Query operations (14 files)
├── infrastructure/
│   ├── IGitPort.ts         # Git port interface
│   ├── SimpleGitAdapter.ts # simple-git implementation
│   └── GitObserverAdapter.ts # Observer adapter
├── checks/
│   └── git-basic-checks.ts # Check definitions
└── register.ts             # Plugin factory (createGitPlugin)
```

### GitState Structure

```typescript
interface GitState {
  // Core fields (always present)
  currentBranch: string | null;
  isDetached: boolean;
  workingDirectory: string;
  protectedBranches: string[];
  isProtectedBranch: boolean;

  // Eagerly loaded optional fields
  status?: GitStatusData;      // Changed/untracked files
  diffStats?: DiffStats;       // Line change statistics
  recentCommits?: CommitInfo[]; // Recent commit history
  remoteStatus?: GitRemoteData; // Remote sync status
  hasMergeCommits?: boolean;   // Merge commit detection
  config?: GitConfigData;      // Git user configuration
}
```

### Async Query API

All queries return `Promise<QueryResponse>`:

```typescript
const observable = await observer.observe({ context: '/path/to/repo' });
const result = await observable.query({
  operation: 'branchIsProtected'
});
// result: { ok: boolean, data: any, message?: string }
```

## API Reference

### createGitPlugin(config?)

Create a C2 plugin descriptor for the git observable.

```typescript
function createGitPlugin(config?: GitObservableConfig): C2PluginDescriptor;

interface GitObservableConfig {
  gitPort?: IGitPort;           // Custom git adapter
  protectedBranches?: string[]; // Protected branch names
  recentCommitCount?: number;   // Commits to retrieve
}
```

### Exported Types

```typescript
// Plugin API
export { createGitPlugin, GIT_PLUGIN_VERSION, GitObservableConfig };

// Profiles
export { gitProfiles, gitProfileRecommended, gitProfileStrict, gitProfileMinimal, gitProfileCI };

// Domain (v2)
export type { GitState, GitStatusData, GitRemoteData, GitConfigData };
export type { CommitInfo };
export { GitObservable, createGitState };
export { matchPattern, matchAnyPattern };

// Infrastructure (for advanced use / testing)
export { IGitPort, SimpleGitAdapter, GitObserverAdapter };

// Check definitions
export { gitCheckDefinitions };
```

## Development

```bash
# Build
npx nx build @garrick0/c2-observable-git

# Test
npx nx test @garrick0/c2-observable-git

# Lint
npx nx lint @garrick0/c2-observable-git
```

## License

MIT