FN-5843: scaffold standalone external-author plugins with reference template

Add a standalone external-author plugin scaffold flow and reference plugin output for fn plugin new.

- add standalone scaffold mode to plugin generator with external-author defaults
- generate reference plugin files and metadata tailored for out-of-tree/plugin-author usage
- update CLI wiring and command behavior for the new scaffold path
- expand CLI and scaffold tests to cover standalone generation behavior
- document the updated plugin scaffolding flow in CLI reference docs

Files changed:
 docs/cli-reference.md                              |   3 +-
 packages/cli/src/__tests__/bin.test.ts             |  12 +-
 packages/cli/src/__tests__/plugin-scaffold.test.ts | 114 ++++++---
 packages/cli/src/bin.ts                            |  18 +-
 packages/cli/src/commands/plugin-scaffold.ts       | 269 +++++++++++++++++++--
 5 files changed, 356 insertions(+), 60 deletions(-)

Fusion-Task-Id: FN-5843

Fusion-Task-Lineage: 4383e9ed-0b12-451e-a946-2d002f7e8faf
This commit is contained in:
gsxdsm
2026-06-01 16:20:52 -07:00
parent af6e5b5eaf
commit 9cbb54906f
5 changed files with 359 additions and 63 deletions

View File

@@ -138,7 +138,7 @@ async function loadCommandHandlers() {
const { runMessageInbox, runMessageOutbox, runMessageSend, runMessageRead, runMessageDelete, runAgentMailbox } = await import("./commands/message.js");
const { runChatInteractive } = await import("./commands/chat.js");
const { runPluginList, runPluginInstall, runPluginUninstall, runPluginEnable, runPluginDisable, runPluginSetupStatus, runPluginSetup, runPluginAvailable, runPluginSettings, runPluginRescan } = await import("./commands/plugin.js");
const { runPluginCreate } = await import("./commands/plugin-scaffold.js");
const { runPluginCreate, runPluginNew } = await import("./commands/plugin-scaffold.js");
const { runSkillsSearch, runSkillsInstall } = await import("./commands/skills.js");
const { runResearchCreate, runResearchList, runResearchShow, runResearchExport, runResearchCancel, runResearchRetry } = await import("./commands/research.js");
const { runExperimentFinalize } = await import("./commands/experiment-finalize.js");
@@ -235,6 +235,7 @@ async function loadCommandHandlers() {
runPluginSettings,
runPluginRescan,
runPluginCreate,
runPluginNew,
runSkillsSearch,
runSkillsInstall,
runResearchCreate,
@@ -390,6 +391,7 @@ PR:
fn plugin setup <id> [--action install|uninstall]
Install or uninstall plugin setup binaries/runtimes
fn plugin create <name> Scaffold a new plugin project
fn plugin new <name> Scaffold a standalone publishable plugin project
fn skills search <query> Search skills.sh for agent skills
fn skills search <query> --limit 5 Limit results
fn skills install <owner/repo> Install skills from a source
@@ -660,6 +662,7 @@ async function main() {
runPluginSettings,
runPluginRescan,
runPluginCreate,
runPluginNew,
runSkillsSearch,
runSkillsInstall,
runResearchCreate,
@@ -1768,12 +1771,21 @@ async function main() {
case "create": {
const pluginName = args[2];
if (!pluginName) { console.error("Usage: fn plugin create <name>"); process.exit(1); }
await runPluginCreate(pluginName);
await runPluginCreate(pluginName, { output: getFlagValue(args.slice(3), "--output") });
break;
}
case "new": {
const pluginName = args[2];
if (!pluginName) { console.error("Usage: fn plugin new <name> [--output <dir>] [--scope <scope>]"); process.exit(1); }
await runPluginNew(pluginName, {
output: getFlagValue(args.slice(3), "--output"),
scope: getFlagValue(args.slice(3), "--scope"),
});
break;
}
default:
console.error(`Unknown subcommand: plugin ${sub || ""}`);
console.log("Try: fn plugin list | install | add (alias for install) | uninstall | enable | disable | available | settings | rescan | setup-status | setup | create");
console.log("Try: fn plugin list | install | add (alias for install) | uninstall | enable | disable | available | settings | rescan | setup-status | setup | create | new");
process.exit(1);
}
break;