EveryInc/compound-engineering-plugin · error · Error

Could not find ${PLUGIN_MANIFEST} under ${inputPath}

Error message

Could not find ${PLUGIN_MANIFEST} under ${inputPath}

What it means

`resolveClaudeRoot()` locates the plugin root from an input path (the root itself, a `.claude-plugin` dir, or a `plugin.json` file). If none of the layouts match — i.e. no `.claude-plugin/plugin.json` manifest can be found under the given path — it throws this error, meaning the input is not a recognizable Claude plugin directory.

Source

Thrown at src/parsers/claude.ts:54

  }
}

async function resolveClaudeRoot(inputPath: string): Promise<string> {
  const absolute = path.resolve(inputPath)
  const manifestAtPath = path.join(absolute, PLUGIN_MANIFEST)
  if (await pathExists(manifestAtPath)) {
    return absolute
  }

  if (absolute.endsWith(PLUGIN_MANIFEST)) {
    return path.dirname(path.dirname(absolute))
  }

  if (absolute.endsWith("plugin.json")) {
    return path.dirname(path.dirname(absolute))
  }

  throw new Error(`Could not find ${PLUGIN_MANIFEST} under ${inputPath}`)
}

async function loadAgents(agentsDirs: string[]): Promise<ClaudeAgent[]> {
  const files = await collectMarkdownFiles(agentsDirs)

  const agents: ClaudeAgent[] = []
  for (const file of files) {
    const raw = await readText(file)
    const { data, body } = parseFrontmatter(raw, file)
    const name = (data.name as string) ?? deriveMarkdownStem(file)
    agents.push({
      name,
      description: data.description as string | undefined,
      capabilities: data.capabilities as string[] | undefined,
      model: data.model as string | undefined,
      body: body.trim(),
      sourcePath: file,
    })

View on GitHub (pinned to c9c10f8c75)

Solutions

  1. Point the input at the plugin root — the directory containing `.claude-plugin/plugin.json`
  2. Verify the file exists: `ls <path>/.claude-plugin/plugin.json`
  3. If you intended a plugin.json file path, pass the path to that file directly
  4. Restore a missing manifest from git or create a valid one

Example fix

// before
await loadClaudePlugin("./skills/ce-plan")
// after: use the plugin root
await loadClaudePlugin("./")
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from "node:fs"
import path from "node:path"
if (!existsSync(path.join(inputPath, ".claude-plugin", "plugin.json"))) {
  throw new Error(`${inputPath} is not a Claude plugin root (no .claude-plugin/plugin.json)`)
}
await loadClaudePlugin(inputPath)

Type guard

function hasClaudeManifest(p: string): boolean { return existsSync(path.join(p, ".claude-plugin", "plugin.json")) }

Try / catch

try {
  const plugin = await loadClaudePlugin(inputPath)
} catch (error) {
  if (String(error).includes("Could not find")) {
    console.error(`${inputPath} is not a Claude plugin root; point at the directory containing .claude-plugin/plugin.json`)
  } else throw error
}

Prevention

When it happens

Trigger: Calling `loadClaudePlugin(inputPath)` (or the `root`/`resolveClaudeRoot` path) with a directory that does not exist, is not a plugin checkout, or lacks `.claude-plugin/plugin.json`; passing a file path other than `plugin.json`; passing the `.claude-plugin` directory of a plugin whose manifest was renamed or removed.

Common situations: Typo in the path or pointing at the repo's `skills/` subdirectory instead of the plugin root; running the converter against a plugin missing its manifest; git sparse/symlink checkout where `.claude-plugin` wasn't materialized.

Related errors


AI-assisted analysis of EveryInc/compound-engineering-plugin@c9c10f8c75 (2026-08-31). Data as JSON: /api/errors/5ef18fc8f6125d29. Report an issue: GitHub.