UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

151 lines 5.42 kB
/** * Gitignore Helper * * Shared module for checking and updating .gitignore files with recommended * AIWG runtime and provider directory patterns. * * @implements #553 * @module src/config/gitignore */ import * as fs from 'fs/promises'; import * as path from 'path'; // =========================== // Recommended Patterns // =========================== /** * AIWG runtime subdirectories that should be gitignored. * NOTE: .aiwg/ itself is intentionally NOT included — it contains * project artifacts (requirements, architecture, etc.) that should be versioned. * Only the high-churn runtime subdirectories are ignored. */ export const AIWG_RUNTIME_PATTERNS = [ '.aiwg/working/', '.aiwg/ralph/', '.aiwg/ralph-external/', ]; /** * Provider conventional directories for non-native deployments. * These are generated by `aiwg use` and should not be version-controlled. * Claude Code native dirs (.claude/agents/, .claude/skills/, etc.) are intentionally * excluded from this list — they are authored artifacts worth versioning. */ export const PROVIDER_CONVENTIONAL_PATTERNS = [ '.codex/', '.cursor/agents/', '.cursor/commands/', '.cursor/skills/', '.factory/skills/', '.factory/rules/', '.warp/', '.windsurf/agents/', '.windsurf/skills/', '.windsurf/rules/', ]; /** * Claude Code session state patterns (machine-local, should not be versioned). */ export const CLAUDE_SESSION_PATTERNS = [ '.claude/settings.local.json', ]; /** All recommended patterns combined */ export const ALL_RECOMMENDED_PATTERNS = [ ...AIWG_RUNTIME_PATTERNS, ...CLAUDE_SESSION_PATTERNS, ...PROVIDER_CONVENTIONAL_PATTERNS, ]; // =========================== // Core Functions // =========================== /** * Check which recommended patterns are missing from the project's .gitignore. */ export async function checkGitignore(projectRoot) { const gitignorePath = path.join(projectRoot, '.gitignore'); let content = ''; let exists = false; try { content = await fs.readFile(gitignorePath, 'utf-8'); exists = true; } catch { // File doesn't exist — all patterns are missing } const lines = content.split('\n').map(l => l.trim()); const isCovered = (pattern) => { // Exact match if (lines.includes(pattern)) return true; // Pattern without trailing slash is also sufficient if (lines.includes(pattern.replace(/\/$/, ''))) return true; // Parent directory is ignored (covers all children) const parts = pattern.split('/').filter(Boolean); for (let i = 1; i < parts.length; i++) { const parent = parts.slice(0, i).join('/') + '/'; if (lines.includes(parent) || lines.includes(parent.replace(/\/$/, ''))) { return true; } } return false; }; const missingRuntime = AIWG_RUNTIME_PATTERNS.filter(p => !isCovered(p)); const missingSession = CLAUDE_SESSION_PATTERNS.filter(p => !isCovered(p)); const missingProvider = PROVIDER_CONVENTIONAL_PATTERNS.filter(p => !isCovered(p)); const missing = [...missingRuntime, ...missingSession, ...missingProvider]; return { exists, missing, missingRuntime, missingSession, missingProvider }; } /** * Append missing patterns to .gitignore, creating the file if it doesn't exist. * Patterns are grouped with comments. */ export async function appendGitignore(projectRoot, patterns) { const gitignorePath = path.join(projectRoot, '.gitignore'); let existing = ''; let fileExists = false; try { existing = await fs.readFile(gitignorePath, 'utf-8'); fileExists = true; } catch { // File doesn't exist, will create } const existingLines = existing.split('\n').map(l => l.trim()); const toAdd = []; const alreadyPresent = []; for (const pattern of patterns) { if (existingLines.includes(pattern) || existingLines.includes(pattern.replace(/\/$/, ''))) { alreadyPresent.push(pattern); } else { toAdd.push(pattern); } } if (toAdd.length > 0) { const runtimeToAdd = toAdd.filter(p => AIWG_RUNTIME_PATTERNS.includes(p)); const sessionToAdd = toAdd.filter(p => CLAUDE_SESSION_PATTERNS.includes(p)); const providerToAdd = toAdd.filter(p => PROVIDER_CONVENTIONAL_PATTERNS.includes(p)); let block = ''; if (runtimeToAdd.length > 0) { block += '\n# AIWG — ignore only high-churn runtime subdirs, NOT .aiwg/ itself\n'; block += runtimeToAdd.join('\n') + '\n'; } if (sessionToAdd.length > 0) { block += '\n# Claude Code local session state\n'; block += sessionToAdd.join('\n') + '\n'; } if (providerToAdd.length > 0) { block += '\n# Agentic provider conventional dirs (generated by aiwg use, not authored)\n'; block += providerToAdd.join('\n') + '\n'; } const newContent = existing.endsWith('\n') || existing === '' ? existing + block : existing + '\n' + block; await fs.writeFile(gitignorePath, newContent, 'utf-8'); } return { added: toAdd, alreadyPresent, created: !fileExists && toAdd.length > 0, }; } //# sourceMappingURL=gitignore.js.map