/** * The bundle = the DEPENDENCY CLOSURE of the plugin root (design v3 §4), not a directory heuristic: * * .claude-plugin/{plugin,archetypes,components,specialist,stack-registry}.json * the manifest + the runtime catalogs * garden's scripts read, BY NAME * (`PLUGIN_MANIFEST_FILES`) — plugin.json, * archetypes.json and components.json * REQUIRED at publish * (`REQUIRED_PLUGIN_CATALOGS`); anything * else in that directory (marketplace.json, * a publish-time listing) is outside * skills/** nested layout VERBATIM — never renamed * scripts/** minus ci/, wg/ and the wg-… dev tools what `${CLAUDE_PLUGIN_ROOT}/scripts/…` resolves * schemas/** `../schemas/evidence.json` links * docs/examples/** `qe/refs/campaign.md` → the copyable workflows * pyproject.toml, uv.lock `uv run` from the plugin root needs them * * Hooks, the site, tests, `.venv`, the operator's `_meta/`, and everything else a plugin checkout * carries never come along. The content hash of THIS set is the baseline's identity. * * # ONE allowlist (codex round 6 on #480; the exact spelling — codex round 7) * * `inBundleClosure` is the closure as a predicate over a plugin-relative path — the single source * three consumers share: the seed/refresh copy (`pluginBundleFiles` keeps only members), the support * file API (`store.resolveSupportFile` refuses a path outside it with an `outside-closure` blocked * envelope) and publish/analyze (a file found under `effective/` outside it — a direct filesystem * edit — is a BLOCKING `outside-closure` finding naming the path). What the seed would not copy, * the API cannot add and a snapshot never ships. The `.claude-plugin` half is an allowlist BY NAME — * design amendment v3.5 §1, adjudicated against the live 12.32.0 layout (six files there; five are * runtime catalogs garden's scripts read — `specialist.json` by `scripts/crew/specialist_discovery.py`, * `stack-registry.json` by the pack/capability registries; `marketplace.json` is a publish-time * listing), superseding the v3 "three files" spelling that predated the catalog audit; `scripts/wg/` * IS a directory in the live plugin, so it is excluded as a directory alongside `ci/`, and the * `wg-…` prefix exclusion stays. * * # No-follow BELOW the source root (codex round 6) * * The plugin root is resolved ONCE (`realPluginRoot` — an operator's symlinked config dir reaches * it legitimately); every designated entry below it — each named manifest file, each root file, * each bundle directory and everything under it — is lstat-walked, and a symlink among them refuses * the whole ingestion by name (`PluginSourceSymlinkError`): nothing is copied. A link inside a * PRUNED subtree (`scripts/ci/`, `node_modules/`, `.venv/`) or at a non-designated name * (`.claude-plugin/marketplace.json`) is never reached — it is not part of the closure, so it is * neither copied nor judged. */ import { type FileRecord } from './tree.js'; /** The plugin subdirectory skills live in (and the prefix of every manifest `dir`). */ export declare const SKILLS_SUBDIR = "skills"; /** The manifest directory. */ export declare const PLUGIN_MANIFEST_DIRNAME = ".claude-plugin"; /** * The files under `.claude-plugin/` that ride the bundle — BY NAME (codex round 7, adjudicated on the * live 12.32.0 layout): the plugin manifest and the four runtime catalogs garden's scripts read. * `marketplace.json` (the marketplace's publish-time listing) and anything else are outside. */ export declare const PLUGIN_MANIFEST_FILES: ReadonlyArray; /** * The runtime catalogs every snapshot MUST carry beside `plugin.json` (design v3 §4): garden's * runtime reads `archetypes.json` (`scripts/crew/archetypes_v11.py:60-74` raises when it is absent) * and `components.json`. Their absence in `effective/` is a BLOCKING publish finding * (`missing-plugin-manifest`) — a seed copies what the source has (a source missing them is a * defective install the publish names, not a seed failure). */ export declare const REQUIRED_PLUGIN_CATALOGS: ReadonlyArray; /** The human spelling of the closure — for findings and refusals. */ export declare const BUNDLE_CLOSURE_SPELLING: string; /** * Whether a plugin-relative POSIX path is a member of the bundle closure (module header: the ONE * allowlist). Lexical — the caller has already validated the path's shape. */ export declare function inBundleClosure(rel: string): boolean; /** The plugin root has no `.claude-plugin/plugin.json`. */ export declare class NotAPluginRootError extends Error { readonly dir: string; constructor(dir: string); } /** * The bundle file set of `pluginRoot`, sorted by `rel` — every member of the closure the source * carries, reached without following a link below the once-resolved root (module header). Throws * `NotAPluginRootError` (no manifest) or `PluginSourceSymlinkError` (a link among the designated * entries — nothing is copied by the caller either). */ export declare function pluginBundleFiles(pluginRoot: string): FileRecord[]; /** crew#1021: discovery probes yield between tree entries so a large install cannot stall requests. */ export declare function pluginBundleFilesPaced(pluginRoot: string): Promise; /** Every `skills/` dir in a file set that holds a `SKILL.md` (`skills/SKILL.md` itself is not a skill). */ export declare function skillDirsOf(files: ReadonlyArray<{ rel: string; }>): Set; /** * The skill dir that OWNS a plugin-relative path: the deepest `skillDirs` entry the path sits * under, or `null` for a support file. Nested-skill ownership (v3 §6) is exactly this rule. */ export declare function owningSkillDir(rel: string, skillDirs: ReadonlySet): string | null; //# sourceMappingURL=bundle.d.ts.map