OpenWriterControle/plugins/claude-code/scripts/utils/git-sparse.ts
Darren Hinde f84f32b7b3
fix: overhaul Claude Code plugin for correctness and spec compliance (#225)
- Fix critical bugs: coder-agent name (CoderAgent→coder-agent), manifest
  path in session-start.sh, /oac:setup ghost command replaced with /install-context
- Fix hooks.json: add timeout/async, add description, restore matcher
- Fix session-start.sh: dual-format output (Claude Code + Cursor/OpenCode),
  remove invalid JSON escapes for $ and backtick
- Fix agent spec compliance: disallowedTools on read-only agents, WebFetch
  on external-scout, haiku model on context-scout, example blocks on 3 agents
- Fix skill descriptions: triggering conditions only (no workflow summaries)
  per superpowers empirical testing — prevents Claude shortcutting skill content
- Add Red Flags + Rationalization tables to 4 discipline skills
- Add install-context skill + command replacing oac-setup/oac-plan/oac-add-context
- Add cleanup-tmp.sh (was missing, broke /oac:cleanup)
- Add TypeScript installer (install-context.ts/js) with profile-based download
- Update plugin.json: keywords, homepage for marketplace discoverability
- Update README: correct skill count, fix structure diagram, remove stale refs
- Remove obsolete files: FIRST-TIME-SETUP.md, INSTALL.md, QUICK-START.md,
  download-context.sh, verify-context.sh, load-config.sh, context-manager skill,
  oac-setup/oac-plan/oac-add-context commands
2026-02-19 20:16:17 +00:00

135 lines
3 KiB
TypeScript

/**
* Git Sparse Checkout Utilities
* Handles git sparse-checkout operations for downloading specific paths
*/
import { execSync } from 'child_process'
import { existsSync, rmSync, mkdirSync } from 'fs'
import { join } from 'path'
export interface GitSparseOptions {
repository: string
branch: string
paths: string[]
targetDir: string
verbose?: boolean
}
export interface GitSparseResult {
success: boolean
commit: string
error?: string
}
/**
* Clone repository with sparse checkout
*/
export async function sparseClone(
options: GitSparseOptions
): Promise<GitSparseResult> {
const { repository, branch, paths, targetDir, verbose } = options
try {
// Clean up target directory if it exists
if (existsSync(targetDir)) {
rmSync(targetDir, { recursive: true, force: true })
}
// Create parent directory
const parentDir = join(targetDir, '..')
if (!existsSync(parentDir)) {
mkdirSync(parentDir, { recursive: true })
}
// Clone with sparse checkout (no working tree files)
if (verbose) {
console.log(`Cloning ${repository}...`)
}
execSync(
`git clone --depth 1 --filter=blob:none --sparse --branch ${branch} https://github.com/${repository}.git "${targetDir}"`,
{ stdio: verbose ? 'inherit' : 'pipe' }
)
// Configure sparse checkout for requested paths
if (verbose) {
console.log('Configuring sparse checkout...')
}
const sparseCheckoutPaths = paths.join(' ')
execSync(`cd "${targetDir}" && git sparse-checkout set ${sparseCheckoutPaths}`, {
stdio: verbose ? 'inherit' : 'pipe',
})
// Get commit SHA
const commit = execSync(`cd "${targetDir}" && git rev-parse HEAD`, {
encoding: 'utf-8',
}).trim()
if (verbose) {
console.log(`Downloaded commit: ${commit}`)
}
return {
success: true,
commit,
}
} catch (error) {
return {
success: false,
commit: '',
error: error instanceof Error ? error.message : String(error),
}
}
}
/**
* Copy files from sparse checkout to target location
*/
export function copyFiles(
sourceDir: string,
targetDir: string,
verbose?: boolean
): void {
if (!existsSync(sourceDir)) {
throw new Error(`Source directory not found: ${sourceDir}`)
}
// Create target directory if it doesn't exist
if (!existsSync(targetDir)) {
mkdirSync(targetDir, { recursive: true })
}
// Copy files
if (verbose) {
console.log(`Copying files from ${sourceDir} to ${targetDir}...`)
}
execSync(`cp -r "${sourceDir}"/* "${targetDir}"/`, {
stdio: verbose ? 'inherit' : 'pipe',
})
}
/**
* Clean up temporary directory
*/
export function cleanup(dir: string, verbose?: boolean): void {
if (existsSync(dir)) {
if (verbose) {
console.log(`Cleaning up ${dir}...`)
}
rmSync(dir, { recursive: true, force: true })
}
}
/**
* Check if git is available
*/
export function checkGitAvailable(): boolean {
try {
execSync('command -v git', { stdio: 'ignore' })
return true
} catch {
return false
}
}