Unify and streamline profile system architecture (#853)

* move claude rules and commands to assets/claude

* update claude profile to copy assets/claude to .claude

* fix formatting

* feat(profiles): Implement unified profile system

- Convert Claude and Codex profiles to use createProfile() factory
- Remove simple vs complex profile distinction in rule transformer
- Unify convertAllRulesToProfileRules() to handle all profiles consistently
- Fix mcpConfigPath construction in base-profile.js for null mcpConfigName
- Update terminology from 'simpleProfiles' to 'assetOnlyProfiles' throughout
- Ensure Claude .claude directory copying works in both CLI and MCP contexts
- All profiles now follow same execution flow with proper lifecycle functions

Changes:
- src/profiles/claude.js: Convert to createProfile() factory pattern
- src/profiles/codex.js: Convert to createProfile() factory pattern
- src/utils/rule-transformer.js: Unified profile handling logic
- src/utils/profiles.js: Remove simple profile categorization
- src/profiles/base-profile.js: Fix mcpConfigPath construction
- scripts/modules/commands.js: Update variable naming
- tests/: Update all tests for unified system and terminology

Fixes Claude profile asset copying issue in MCP context.
All tests passing (617 passed, 11 skipped).

* re-checkin claude files

* fix formatting

* chore: clean up test Claude rules files

* chore: add changeset for unified profile system

* add claude files back

* add changeset

* restore proper gitignore

* remove claude agents file from root

* remove incorrect doc

* simplify profiles and update tests

* update changeset

* update changeset

* remove profile specific code

* streamline profiles with defaults and update tests

* update changeset

* add newline at end of gitignore

* restore changes

* streamline profiles with defaults; update tests and add vscode test

* update rule profile tests

* update wording for clearer profile management

* refactor and clarify terminology

* use original projectRoot var name

* revert param desc

* use updated claude assets from neno

* add "YOUR_" before api key here

* streamline codex profile

* add gemini profile

* update gemini profile

* update tests

* relocate function

* update rules interactive setup Gemini desc

* remove duplicative code

* add comma
This commit is contained in:
Joe Danziger
2025-07-09 07:22:11 -04:00
committed by GitHub
parent 5f009a5e1f
commit 95c299df64
82 changed files with 4827 additions and 720 deletions

View File

@@ -2,58 +2,96 @@
import path from 'path';
import fs from 'fs';
import { isSilentMode, log } from '../../scripts/modules/utils.js';
import { createProfile } from './base-profile.js';
// Helper function to recursively copy directory (adopted from Roo profile)
function copyRecursiveSync(src, dest) {
const exists = fs.existsSync(src);
const stats = exists && fs.statSync(src);
const isDirectory = exists && stats.isDirectory();
if (isDirectory) {
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
fs.readdirSync(src).forEach((childItemName) => {
copyRecursiveSync(
path.join(src, childItemName),
path.join(dest, childItemName)
);
});
} else {
fs.copyFileSync(src, dest);
}
}
// Helper function to recursively remove directory
function removeDirectoryRecursive(dirPath) {
if (fs.existsSync(dirPath)) {
try {
fs.rmSync(dirPath, { recursive: true, force: true });
return true;
} catch (err) {
log('error', `Failed to remove directory ${dirPath}: ${err.message}`);
return false;
}
}
return true;
}
// Lifecycle functions for Claude Code profile
function onAddRulesProfile(targetDir, assetsDir) {
// Use the provided assets directory to find the source file
const sourceFile = path.join(assetsDir, 'AGENTS.md');
const destFile = path.join(targetDir, 'CLAUDE.md');
// Copy .claude directory recursively
const claudeSourceDir = path.join(assetsDir, 'claude');
const claudeDestDir = path.join(targetDir, '.claude');
if (fs.existsSync(sourceFile)) {
try {
fs.copyFileSync(sourceFile, destFile);
log('debug', `[Claude] Copied AGENTS.md to ${destFile}`);
} catch (err) {
log('error', `[Claude] Failed to copy AGENTS.md: ${err.message}`);
}
if (!fs.existsSync(claudeSourceDir)) {
log(
'error',
`[Claude] Source directory does not exist: ${claudeSourceDir}`
);
return;
}
try {
copyRecursiveSync(claudeSourceDir, claudeDestDir);
log('debug', `[Claude] Copied .claude directory to ${claudeDestDir}`);
} catch (err) {
log(
'error',
`[Claude] An error occurred during directory copy: ${err.message}`
);
}
}
function onRemoveRulesProfile(targetDir) {
const claudeFile = path.join(targetDir, 'CLAUDE.md');
if (fs.existsSync(claudeFile)) {
try {
fs.rmSync(claudeFile, { force: true });
log('debug', `[Claude] Removed CLAUDE.md from ${claudeFile}`);
} catch (err) {
log('error', `[Claude] Failed to remove CLAUDE.md: ${err.message}`);
}
// Remove .claude directory recursively
const claudeDir = path.join(targetDir, '.claude');
if (removeDirectoryRecursive(claudeDir)) {
log('debug', `[Claude] Removed .claude directory from ${claudeDir}`);
}
}
function onPostConvertRulesProfile(targetDir, assetsDir) {
// For Claude, post-convert is the same as add since we don't transform rules
onAddRulesProfile(targetDir, assetsDir);
}
// Simple filename function
function getTargetRuleFilename(sourceFilename) {
return sourceFilename;
}
// Simple profile configuration - bypasses base-profile system
export const claudeProfile = {
profileName: 'claude',
// Create and export claude profile using the base factory
export const claudeProfile = createProfile({
name: 'claude',
displayName: 'Claude Code',
url: 'claude.ai',
docsUrl: 'docs.anthropic.com/en/docs/claude-code',
profileDir: '.', // Root directory
rulesDir: '.', // No rules directory needed
mcpConfig: false, // No MCP config needed
rulesDir: '.', // No specific rules directory needed
mcpConfig: false,
mcpConfigName: null,
mcpConfigPath: null,
conversionConfig: {},
fileMap: {},
globalReplacements: [],
getTargetRuleFilename,
onAddRulesProfile,
onRemoveRulesProfile,
onPostConvertRulesProfile
};
includeDefaultRules: false,
fileMap: {
'AGENTS.md': 'CLAUDE.md'
},
onAdd: onAddRulesProfile,
onRemove: onRemoveRulesProfile,
onPostConvert: onPostConvertRulesProfile
});
// Export lifecycle functions separately to avoid naming conflicts
export { onAddRulesProfile, onRemoveRulesProfile, onPostConvertRulesProfile };