Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 53 additions & 22 deletions .claude/skills/tech-lead/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,36 +215,54 @@ Alternatively, proceed with implementation if user accepts the complexity. Ask u

### Phase 5: Implementation Start

**Step 7: Branch Creation and Implementation**
**Step 7: Branch Protection Check and Implementation**

If user agrees to proceed with implementation:

**CRITICAL: Always create feature branch before any code changes**
**CRITICAL: NEVER implement on main/master. Always check branch first.**

1. **Create Feature Branch:**
```bash
# Check current branch
git branch --show-current
**7a. Check Current Branch (MANDATORY FIRST STEP):**
```bash
git branch --show-current
```

**7b. If on `main` or `master` branch — STOP immediately:**

Do NOT write any code. Display this message:

```markdown
⚠️ **Branch Protection**: You are currently on the `main` branch.

# Create and switch to feature branch
# For Jira tickets:
git checkout -b EPMCDME-XXXXX
Implementation cannot start on `main`. I will create a feature branch first.

# For non-Jira tasks (use determined branch name):
git checkout -b feature/branch-name
```
Creating branch: `[determined-branch-name]`...
```

Then create the feature branch immediately before touching any files.

2. **Verify Branch:**
```bash
# Confirm on new branch
git branch --show-current
```
**7c. If already on a feature branch — proceed normally.**

3. **Begin Implementation:**
- Follow patterns identified in analysis phase
- Reference guides for standard approaches
- Implement changes layer by layer (API → Service → Repository)
- Apply security and performance patterns
**7d. Create Feature Branch (if on main/master or no branch exists):**
```bash
# Create and switch to feature branch
# For Jira tickets:
git checkout -b EPMCDME-XXXXX

# For non-Jira tasks (use determined branch name):
git checkout -b feature/branch-name
```

**7e. Verify Branch:**
```bash
# Confirm on correct branch (must NOT be main or master)
git branch --show-current
```

**7f. Begin Implementation:**
- Follow patterns identified in analysis phase
- Reference guides for standard approaches
- Implement changes layer by layer (API → Service → Repository)
- Apply security and performance patterns

**Branch Naming:**

Expand Down Expand Up @@ -283,6 +301,7 @@ For non-Jira tasks:
❌ Don't skip guide consultation
❌ Don't ask generic clarifying questions
❌ Don't start coding without branch creation
❌ Don't write ANY code while on `main` or `master` branch
❌ Don't make architectural decisions for complex features without user input
❌ Don't fetch unnecessary Jira fields (when using Jira)
❌ Don't guess at complexity—analyze systematically
Expand Down Expand Up @@ -382,6 +401,18 @@ Unable to fetch Jira ticket [ID]. Please verify:
- You have access to view this ticket
```

### Currently on Main Branch

When `git branch --show-current` returns `main` or `master`:

```markdown
⚠️ **Branch Protection**: You are currently on the `main` branch.

Implementation cannot start on `main`. Creating feature branch `[branch-name]` now...
```

Then immediately run `git checkout -b [branch-name]` before any file edits.

### Branch Already Exists
```markdown
Branch [EPMCDME-XXXXX] already exists.
Expand Down
23 changes: 6 additions & 17 deletions src/agents/core/extension/BaseExtensionInstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { mkdir, cp, access, readFile, writeFile, readdir, stat, rm } from 'fs/pr
import { join, dirname, relative } from 'path';
import { constants, existsSync } from 'fs';
import { logger } from '../../../utils/logger.js';
import { normalizePathSeparators } from '../../../utils/paths.js';
import { normalizePathSeparators, getCodemiePath } from '../../../utils/paths.js';

/**
* Configuration for selective local file copying
Expand Down Expand Up @@ -460,7 +460,7 @@ export abstract class BaseExtensionInstaller {
targetPath: string,
version: string | null
): Promise<void> {
const versionFile = join(targetPath, `${this.agentName}.extension.json`);
const versionFile = getCodemiePath(`${this.agentName}.extension.json`);

const versionInfo = {
version: version || 'unknown',
Expand All @@ -478,10 +478,8 @@ export abstract class BaseExtensionInstaller {
/**
* Check if local extension should be updated
*
* Performs integrity checks before trusting version file:
* 1. Verifies target directory exists
* 2. Verifies directory contains actual files (not just version file)
* 3. Compares versions only if directory is valid
* 2. Compares source version against ~/.codemie/<agent>.extension.json
*
* @param targetPath - Target directory path
* @param sourceVersion - Source extension version
Expand All @@ -491,7 +489,7 @@ export abstract class BaseExtensionInstaller {
targetPath: string,
sourceVersion: string
): Promise<boolean> {
const versionFile = join(targetPath, `${this.agentName}.extension.json`);
const versionFile = getCodemiePath(`${this.agentName}.extension.json`);

try {
// 1. Check if target directory exists
Expand All @@ -501,21 +499,12 @@ export abstract class BaseExtensionInstaller {
return true;
}

// 2. Verify directory has actual content (not just version file)
const entries = await readdir(targetPath);
const actualFiles = entries.filter(entry => entry !== `${this.agentName}.extension.json`);

if (actualFiles.length === 0) {
logger.debug(`[${this.agentName}] Target directory is empty (no files besides version), will install`);
return true;
}

// 3. Now check version file (directory exists and has files)
// 2. Check version file in ~/.codemie/
const content = await readFile(versionFile, 'utf-8');
const versionInfo = JSON.parse(content);

if (versionInfo.version === sourceVersion) {
logger.debug(`[${this.agentName}] Local extension already at v${sourceVersion} with ${actualFiles.length} files`);
logger.debug(`[${this.agentName}] Local extension already at v${sourceVersion}`);
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,33 +51,33 @@ describe('BaseExtensionInstaller - Windows Path Handling', () => {
describe('Glob Pattern Matching', () => {
describe('Windows Path Scenarios', () => {
it('should match Windows path after normalization', () => {
const windowsPath = 'claude-templates\\README.md';
const windowsPath = 'sound\\notify.mp3';
const normalized = normalizePathSeparators(windowsPath);
const pattern = 'claude-templates/**';
const pattern = 'sound/**';

expect(matchesPattern(normalized, pattern)).toBe(true);
});

it('should match nested Windows paths after normalization', () => {
const windowsPath = 'claude-templates\\guides\\testing\\patterns.md';
const windowsPath = 'sound\\themes\\dark\\complete.mp3';
const normalized = normalizePathSeparators(windowsPath);
const pattern = 'claude-templates/**';
const pattern = 'sound/**';

expect(matchesPattern(normalized, pattern)).toBe(true);
});

it('should not match Windows path WITHOUT normalization (bug scenario)', () => {
const windowsPath = 'claude-templates\\README.md'; // Backslashes
const pattern = 'claude-templates/**'; // Forward slashes
const windowsPath = 'sound\\notify.mp3'; // Backslashes
const pattern = 'sound/**'; // Forward slashes

// This was the bug: Windows paths don't match forward-slash patterns
expect(matchesPattern(windowsPath, pattern)).toBe(false);
});

it('should match Windows path WITH normalization (fix scenario)', () => {
const windowsPath = 'claude-templates\\README.md';
const windowsPath = 'sound\\notify.mp3';
const normalized = normalizePathSeparators(windowsPath);
const pattern = 'claude-templates/**';
const pattern = 'sound/**';

// After normalization, pattern matching works
expect(matchesPattern(normalized, pattern)).toBe(true);
Expand All @@ -86,60 +86,60 @@ describe('BaseExtensionInstaller - Windows Path Handling', () => {

describe('Unix Path Scenarios', () => {
it('should match Unix paths (already use forward slashes)', () => {
const unixPath = 'claude-templates/README.md';
const pattern = 'claude-templates/**';
const unixPath = 'sound/notify.mp3';
const pattern = 'sound/**';

expect(matchesPattern(unixPath, pattern)).toBe(true);
});

it('should match nested Unix paths', () => {
const unixPath = 'claude-templates/guides/testing/patterns.md';
const pattern = 'claude-templates/**';
const unixPath = 'sound/themes/dark/complete.mp3';
const pattern = 'sound/**';

expect(matchesPattern(unixPath, pattern)).toBe(true);
});
});

describe('Pattern Variations', () => {
it('should match single wildcard', () => {
const path = 'claude-templates/README.md';
const pattern = 'claude-templates/*.md';
const path = 'sound/notify.mp3';
const pattern = 'sound/*.mp3';

expect(matchesPattern(path, pattern)).toBe(true);
});

it('should match double wildcard (recursive)', () => {
const path = 'claude-templates/guides/security/patterns.md';
const pattern = 'claude-templates/**/*.md';
const path = 'sound/themes/dark/complete.mp3';
const pattern = 'sound/**/*.mp3';

expect(matchesPattern(path, pattern)).toBe(true);
});

it('should match question mark wildcard', () => {
const path = 'claude-templates/test1.md';
const pattern = 'claude-templates/test?.md';
const path = 'sound/alert1.mp3';
const pattern = 'sound/alert?.mp3';

expect(matchesPattern(path, pattern)).toBe(true);
});
});

describe('Exclusion Patterns', () => {
it('should exclude DS_Store files', () => {
const path = 'claude-templates/.DS_Store';
const path = 'sound/.DS_Store';
const pattern = '**/.DS_Store';

expect(matchesPattern(path, pattern)).toBe(true);
});

it('should exclude test files', () => {
const path = 'claude-templates/utils.test.js';
const path = 'sound/utils.test.js';
const pattern = '**/*.test.js';

expect(matchesPattern(path, pattern)).toBe(true);
});

it('should exclude node_modules', () => {
const path = 'claude-templates/node_modules/package.json';
const path = 'sound/node_modules/package.json';
const pattern = '**/node_modules/**';

expect(matchesPattern(path, pattern)).toBe(true);
Expand All @@ -148,15 +148,15 @@ describe('BaseExtensionInstaller - Windows Path Handling', () => {
});

describe('Real-World Windows Scenarios', () => {
it('should handle typical Claude templates structure on Windows', () => {
it('should handle typical sound assets structure on Windows', () => {
const windowsPaths = [
'claude-templates\\README.md',
'claude-templates\\templates\\CLAUDE.md.template',
'claude-templates\\templates\\guides\\testing\\testing-patterns.md.template',
'claude-templates\\templates\\guides\\security\\security-practices.md.template',
'sound\\notify.mp3',
'sound\\alert.wav',
'sound\\themes\\dark\\complete.mp3',
'sound\\themes\\light\\error.wav',
];

const pattern = 'claude-templates/**';
const pattern = 'sound/**';

// All paths should match after normalization
windowsPaths.forEach(path => {
Expand All @@ -167,9 +167,9 @@ describe('BaseExtensionInstaller - Windows Path Handling', () => {

it('should exclude unwanted files even with Windows paths', () => {
const windowsPaths = [
'claude-templates\\.DS_Store',
'claude-templates\\node_modules\\package.json',
'claude-templates\\utils.test.js',
'sound\\.DS_Store',
'sound\\node_modules\\package.json',
'sound\\utils.test.js',
];

const excludePatterns = ['**/.DS_Store', '**/node_modules/**', '**/*.test.js'];
Expand All @@ -186,7 +186,7 @@ describe('BaseExtensionInstaller - Windows Path Handling', () => {
});

describe('Hybrid Strategy (Include + Exclude)', () => {
const includes = ['claude-templates/**'];
const includes = ['sound/**'];
const excludes = ['**/.DS_Store', '**/node_modules/**', '**/*.test.js'];

const shouldInclude = (path: string): boolean => {
Expand All @@ -208,21 +208,21 @@ describe('BaseExtensionInstaller - Windows Path Handling', () => {
return !excluded;
};

it('should include valid template files', () => {
expect(shouldInclude('claude-templates\\README.md')).toBe(true);
expect(shouldInclude('claude-templates\\templates\\CLAUDE.md.template')).toBe(true);
it('should include valid sound files', () => {
expect(shouldInclude('sound\\notify.mp3')).toBe(true);
expect(shouldInclude('sound\\themes\\dark\\complete.mp3')).toBe(true);
});

it('should exclude DS_Store files', () => {
expect(shouldInclude('claude-templates\\.DS_Store')).toBe(false);
expect(shouldInclude('sound\\.DS_Store')).toBe(false);
});

it('should exclude test files', () => {
expect(shouldInclude('claude-templates\\utils.test.js')).toBe(false);
expect(shouldInclude('sound\\utils.test.js')).toBe(false);
});

it('should exclude node_modules', () => {
expect(shouldInclude('claude-templates\\node_modules\\package.json')).toBe(false);
expect(shouldInclude('sound\\node_modules\\package.json')).toBe(false);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
"enabled": true,
"strategy": "hybrid",
"includes": [
"sound/**",
"claude-templates/**"
"sound/**"
],
"excludes": [
"**/*.test.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"name": "AI/Run CodeMie",
"email": "support@codemieai.com"
},
"version": "1.0.15"
"version": "1.0.16"
}
6 changes: 3 additions & 3 deletions src/agents/plugins/claude/plugin/commands/codemie-init.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Analyze any software project and generate AI-optimized documentation for Claude

- [ ] Project is cloned and accessible
- [ ] Read access to the codebase
- [ ] Templates available at `.codemie/claude-templates/templates/`
- [ ] Templates available at `${CLAUDE_PLUGIN_ROOT}/claude-templates/templates/`

---

Expand Down Expand Up @@ -256,7 +256,7 @@ mkdir -p .codemie/guides
**For each guide in approved list**:

**3.2.1: Load Template**
- Read from `.codemie/claude-templates/templates/guides/[category]/[guide].md.template`
- Read from `${CLAUDE_PLUGIN_ROOT}/claude-templates/templates/guides/[category]/[guide].md.template`
- If no specific template exists, use category base template

**3.2.2: Analyze Codebase for This Guide**
Expand Down Expand Up @@ -318,7 +318,7 @@ Security:

#### Step 4.1: Load Template

- Read `.codemie/claude-templates/templates/CLAUDE.md.template`
- Read `${CLAUDE_PLUGIN_ROOT}/claude-templates/templates/CLAUDE.md.template`

---

Expand Down
Loading