feat: add --manifest flag to pack command (#87)

Allow specifying an external manifest file path with `mcpb pack --manifest <path>`.
The manifest resolves relative to CWD, is validated, injected into the bundle,
and errors immediately if the specified file is not found.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Thompson 2026-03-15 06:54:22 -05:00
commit f4671638a2
No known key found for this signature in database
GPG key ID: 1D66D91917EE02E6
2 changed files with 127 additions and 18 deletions

View file

@ -101,22 +101,33 @@ program
program
.command("pack [directory] [output]")
.description("Pack a directory into an MCPB extension")
.action((directory: string = process.cwd(), output?: string) => {
void (async () => {
try {
const success = await packExtension({
extensionPath: directory,
outputPath: output,
});
process.exit(success ? 0 : 1);
} catch (error) {
console.error(
`ERROR: ${error instanceof Error ? error.message : "Unknown error"}`,
);
process.exit(1);
}
})();
});
.option(
"-m, --manifest <path>",
"Path to manifest file (defaults to manifest.json in directory)",
)
.action(
(
directory: string = process.cwd(),
output: string | undefined,
options: { manifest?: string },
) => {
void (async () => {
try {
const success = await packExtension({
extensionPath: directory,
outputPath: output,
manifestPath: options.manifest,
});
process.exit(success ? 0 : 1);
} catch (error) {
console.error(
`ERROR: ${error instanceof Error ? error.message : "Unknown error"}`,
);
process.exit(1);
}
})();
},
);
// Unpack command
program

View file

@ -22,6 +22,7 @@ interface PackOptions {
extensionPath: string;
outputPath?: string;
silent?: boolean;
manifestPath?: string;
}
function formatFileSize(bytes: number): string {
@ -50,6 +51,7 @@ export async function packExtension({
extensionPath,
outputPath,
silent,
manifestPath: customManifestPath,
}: PackOptions): Promise<boolean> {
const resolvedPath = resolve(extensionPath);
const logger = getLogger({ silent });
@ -60,9 +62,18 @@ export async function packExtension({
return false;
}
// Check if manifest exists
const manifestPath = join(resolvedPath, "manifest.json");
// Resolve manifest path
const manifestPath = customManifestPath
? resolve(customManifestPath)
: join(resolvedPath, "manifest.json");
if (!existsSync(manifestPath)) {
if (customManifestPath) {
// When --manifest is explicitly provided, error immediately
logger.error(`ERROR: Manifest file not found: ${customManifestPath}`);
return false;
}
logger.log(`No manifest.json found in ${extensionPath}`);
const shouldInit = await confirm({
message: "Would you like to create a manifest.json file?",
@ -135,6 +146,15 @@ export async function packExtension({
mcpbIgnorePatterns,
);
// When using a custom manifest path, inject the manifest into the bundle
if (customManifestPath) {
const manifestStat = statSync(manifestPath);
files["manifest.json"] = {
data: readFileSync(manifestPath),
mode: manifestStat.mode,
};
}
// Print package header
logger.log(`\n📦 ${manifest.name}@${manifest.version}`);