Skip to main content
The installation subsystem handles copying facet resources into a project and removing them.

installFacet(name, projectRoot, options?)

Installs a facet by resolving it (local first, then cache), running prerequisite checks, and copying resources to .opencode/.
import { installFacet } from '@ex-machina/facets'

const result = await installFacet('viper', '/path/to/project', {
  onPrereqApproval: async (commands) => {
    console.log('Prerequisites:', commands)
    return true // approve
  },
})

if (result.success) {
  for (const r of result.resources) {
    console.log(`  ${r.type}: ${r.name}`)
  }
}
Parameters:
ParameterTypeDescription
namestringFacet name
projectRootstringProject root directory
optionsInstallOptionsOptional configuration

InstallOptions

OptionTypeDefaultDescription
skipPrereqApprovalbooleanfalseSkip interactive prereq approval
forcePrereqCheckbooleanfalseRe-run prereq checks even if previously confirmed
onPrereqApproval(commands: string[]) => Promise<boolean>Callback to ask user for approval. Must return true to proceed.

Return type

Returns Promise<InstallResult>, a discriminated union:
// Success
interface InstallSuccess {
  success: true
  facet: string
  resources: { name: string; type: string }[]
}

// Facet not found in local sources or cache
interface InstallNotFound {
  success: false
  facet: string
  reason: 'not_found'
}

// A prerequisite command failed
interface InstallPrereqFailure {
  success: false
  facet: string
  reason: 'prereq'
  failure: { command: string; exitCode: number; output: string }
}

// A resource file couldn't be copied
interface InstallCopyFailure {
  success: false
  facet: string
  reason: 'copy'
  error: string
}

type InstallResult = InstallSuccess | InstallNotFound | InstallPrereqFailure | InstallCopyFailure

Resource processing

ResourceProcessing
SkillsCopied verbatim from source to .opencode/skills/<name>/SKILL.md
AgentsPrompt body + assembled YAML frontmatter (description, tools) written to .opencode/agents/<name>.md
CommandsPrompt body + assembled YAML frontmatter (description) written to .opencode/commands/<name>.md
ToolsCopied verbatim from source to .opencode/tools/<name>.ts

uninstallFacet(name, projectRoot)

Removes all installed resources for a facet and cleans up facets.yaml.
import { uninstallFacet } from '@ex-machina/facets'

const result = await uninstallFacet('viper', '/path/to/project')
if (result.success) {
  console.log(`Removed ${result.removed.length} resources`)
}
Returns: Promise<UninstallResult>
// Success
interface UninstallSuccess {
  success: true
  facet: string
  removed: { name: string; type: string }[]
}

// Facet manifest not found
interface UninstallNotFound {
  success: false
  facet: string
  reason: 'not_found'
}

// Filesystem error during removal
interface UninstallError {
  success: false
  facet: string
  reason: 'error'
  error: string
}

type UninstallResult = UninstallSuccess | UninstallNotFound | UninstallError
Side effects:
  • Deletes resource files from .opencode/
  • Removes skill directories recursively
  • Removes the facet entry from facets.yaml (both remote and local)
  • Does not delete the cached copy from ~/.cache/facets/