OpenWriterControle/plugins/claude-code/scripts/utils/registry-fetcher.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

119 lines
2.8 KiB
TypeScript

/**
* Registry Fetcher
* Fetches and parses registry.json from GitHub
*/
import { RegistrySchema, type Registry, type Component, type Profile } from '../types/registry'
export interface FetchRegistryOptions {
source?: 'github' | 'local'
repository?: string
branch?: string
localPath?: string
}
/**
* Fetch registry from GitHub or local file
*/
export async function fetchRegistry(
options: FetchRegistryOptions = {}
): Promise<Registry> {
const {
source = 'github',
repository = 'darrenhinde/OpenAgentsControl',
branch = 'main',
localPath,
} = options
let registryData: unknown
if (source === 'github') {
// Fetch from GitHub raw URL
const url = `https://raw.githubusercontent.com/${repository}/${branch}/registry.json`
const response = await fetch(url)
if (!response.ok) {
throw new Error(`Failed to fetch registry: ${response.statusText}`)
}
registryData = await response.json()
} else {
// Load from local file
if (!localPath) {
throw new Error('localPath required for local source')
}
const fs = await import('fs/promises')
const content = await fs.readFile(localPath, 'utf-8')
registryData = JSON.parse(content)
}
// Validate with Zod
const registry = RegistrySchema.parse(registryData)
return registry
}
/**
* Filter context components by profile
*/
export function filterContextByProfile(
registry: Registry,
profile: Profile
): Component[] {
const contexts = registry.components.contexts || []
if (profile === 'all') {
return contexts
}
// Map profiles to categories
const profileCategories: Record<Profile, string[]> = {
essential: ['essential', 'core'],
standard: ['essential', 'core', 'standard'],
extended: ['essential', 'core', 'standard', 'extended'],
specialized: ['essential', 'core', 'standard', 'extended', 'specialized'],
all: [], // handled above
}
const categories = profileCategories[profile]
if (!categories) {
throw new Error(`Unknown profile: ${profile}`)
}
return contexts.filter((component) => categories.includes(component.category))
}
/**
* Filter context components by custom IDs
*/
export function filterContextByIds(
registry: Registry,
componentIds: string[]
): Component[] {
const contexts = registry.components.contexts || []
return contexts.filter((component) => componentIds.includes(component.id))
}
/**
* Get unique paths from components
*/
export function getUniquePaths(components: Component[]): string[] {
const paths = new Set<string>()
for (const component of components) {
// Extract directory path from component path
const pathParts = component.path.split('/')
pathParts.pop() // Remove filename
const dirPath = pathParts.join('/')
if (dirPath) {
paths.add(dirPath)
}
}
return Array.from(paths)
}