[{"data":1,"prerenderedAt":494},["ShallowReactive",2],{"search-en":3,"content-en-developer\u002Fplugin-cli":4,"surround-en-\u002Fdeveloper\u002Fplugin-cli":485},[],{"id":5,"title":6,"body":7,"description":477,"extension":478,"meta":479,"navigation":480,"path":481,"seo":482,"stem":483,"__hash__":484},"content_en\u002F3.developer\u002F45.plugin-cli.md","The owlat plugins CLI",{"type":8,"value":9,"toc":468},"minimark",[10,30,33,74,82,87,90,145,149,180,203,217,225,262,269,278,285,310,315,354,361,364,405,426,431,443,449,453,464],[11,12,13,17,18,21,22,25,26,29],"p",{},[14,15,16],"code",{},"@owlat\u002Fplugin-cli"," (binary ",[14,19,20],{},"owlat-plugins",") is the lifecycle tool for the bundled plugin set. It operates on the checked-in ",[14,23,24],{},"plugins.config.ts"," composition point and delegates every composition decision to ",[14,27,28],{},"@owlat\u002Fplugin-codegen"," — it never re-implements composition.",[11,31,32],{},"Run it from anywhere inside the Owlat workspace. From a source checkout:",[34,35,40],"pre",{"className":36,"code":37,"language":38,"meta":39,"style":39},"language-sh shiki shiki-themes github-light github-dark-dimmed","bun run plugins:prepare                        # build the plugin-kit contracts once\nbun packages\u002Fplugin-cli\u002Fsrc\u002Findex.ts --help\n","sh","",[14,41,42,62],{"__ignoreMap":39},[43,44,47,51,55,58],"span",{"class":45,"line":46},"line",1,[43,48,50],{"class":49},"sOLd2","bun",[43,52,54],{"class":53},"s-HuK"," run",[43,56,57],{"class":53}," plugins:prepare",[43,59,61],{"class":60},"sDN9O","                        # build the plugin-kit contracts once\n",[43,63,65,67,70],{"class":45,"line":64},2,[43,66,50],{"class":49},[43,68,69],{"class":53}," packages\u002Fplugin-cli\u002Fsrc\u002Findex.ts",[43,71,73],{"class":72},"sviXB"," --help\n",[34,75,80],{"className":76,"code":78,"language":79,"meta":39},[77],"language-text","owlat plugins — manage bundled Owlat plugins\n\nUsage: owlat plugins \u003Ccommand> [options]\n\nCommands:\n  create \u003Cplugin-id> [--name \u003Cpackage>] [--dir \u003Cpath>] [--template \u003Cname>]\n         [--dry-run]\n      Scaffold a new plugin package (never installs or executes code).\n      --template minimal (default) emits an empty manifest; --template\n      send-provider emits a complete send-transport bundle: send module,\n      feedback webhook, sending-domain identity, capabilities and test stubs.\n  add \u003Cpackage> [--dry-run]\n      Add a bundled plugin package to plugins.config.ts and preview its\n      capability diff. --dry-run previews without writing.\n  remove \u003Cpackage> [--dry-run]\n      Remove a bundled plugin package from plugins.config.ts.\n  codegen [--check] [--boundaries-only]\n      Regenerate (or check) the bundled composition via @owlat\u002Fplugin-codegen.\n  dev\n      Regenerate the composition and re-run on every plugins.config.ts change.\n\nRun from anywhere inside the Owlat workspace.\n","text",[14,81,78],{"__ignoreMap":39},[83,84,86],"h2",{"id":85},"safety-properties","Safety properties",[11,88,89],{},"These hold for every command, and they are what make the CLI safe to run against a production checkout:",[91,92,93,104,115,121,135],"ul",{},[94,95,96,103],"li",{},[97,98,99,100,102],"strong",{},"It never evaluates ",[14,101,24],{}," as code."," The config is parsed as static data.",[94,105,106,109,110,114],{},[97,107,108],{},"It never imports an arbitrary path."," The only module loading it performs is delegated to the codegen's verified loader, which imports exclusively the lockfile-pinned, provenance-checked ",[111,112,113],"em",{},"manifest"," entry of each bundled package — never a contribution or component module.",[94,116,117,120],{},[97,118,119],{},"Validation precedes mutation."," The proposed package set is fully loaded and validated before any file is written; a missing, mis-pinned, or invalid package fails with an actionable error and an unchanged config.",[94,122,123,126,127,130,131,134],{},[97,124,125],{},"Edits are deterministic and idempotent."," Re-running produces byte-identical output; an already-listed ",[14,128,129],{},"add"," (or an absent ",[14,132,133],{},"remove",") is a reported no-op that writes nothing.",[94,136,137,140,141,144],{},[97,138,139],{},"Failures roll back."," ",[14,142,143],{},"create"," removes every file and directory it created if any write fails; a failed config write leaves the config unchanged.",[83,146,147],{"id":143},[14,148,143],{},[34,150,152],{"className":36,"code":151,"language":38,"meta":39,"style":39},"owlat-plugins create hello-owlat [--name @acme\u002Fowlat-hello] [--dir plugins\u002Fhello] [--dry-run]\n",[14,153,154],{"__ignoreMap":39},[43,155,156,158,161,164,168,171,174,177],{"class":45,"line":46},[43,157,20],{"class":49},[43,159,160],{"class":53}," create",[43,162,163],{"class":53}," hello-owlat",[43,165,167],{"class":166},"sYgZi"," [--name ",[43,169,170],{"class":53},"@acme\u002Fowlat-hello]",[43,172,173],{"class":166}," [--dir ",[43,175,176],{"class":53},"plugins\u002Fhello]",[43,178,179],{"class":166}," [--dry-run]\n",[11,181,182,183,186,187,186,190,186,193,186,196,186,199,202],{},"Scaffolds a plugin package: ",[14,184,185],{},"package.json",", ",[14,188,189],{},"tsconfig.json",[14,191,192],{},"vitest.config.ts",[14,194,195],{},"README.md",[14,197,198],{},"src\u002Fmanifest.ts",[14,200,201],{},"src\u002Findex.ts",", and a manifest test. Content is a pure function of the id, package name, and the directory's position in the workspace — no timestamps, no randomness.",[11,204,205,206,186,209,212,213,216],{},"Defaults: ",[14,207,208],{},"--name @owlat\u002Fplugin-\u003Cid>",[14,210,211],{},"--dir examples\u002Fplugins\u002F\u003Cid>",". Re-running on an unchanged scaffold is a no-op; a file that exists with ",[97,214,215],{},"different"," content aborts the run rather than clobbering it.",[83,218,220,222,223],{"id":219},"add-remove",[14,221,129],{}," \u002F ",[14,224,133],{},[34,226,228],{"className":36,"code":227,"language":38,"meta":39,"style":39},"owlat-plugins add @vendor\u002Fowlat-plugin-example --dry-run\nowlat-plugins add @vendor\u002Fowlat-plugin-example\nowlat-plugins remove @vendor\u002Fowlat-plugin-example\n",[14,229,230,243,252],{"__ignoreMap":39},[43,231,232,234,237,240],{"class":45,"line":46},[43,233,20],{"class":49},[43,235,236],{"class":53}," add",[43,238,239],{"class":53}," @vendor\u002Fowlat-plugin-example",[43,241,242],{"class":72}," --dry-run\n",[43,244,245,247,249],{"class":45,"line":64},[43,246,20],{"class":49},[43,248,236],{"class":53},[43,250,251],{"class":53}," @vendor\u002Fowlat-plugin-example\n",[43,253,255,257,260],{"class":45,"line":254},3,[43,256,20],{"class":49},[43,258,259],{"class":53}," remove",[43,261,251],{"class":53},[11,263,264,265,268],{},"Both print a ",[97,266,267],{},"capability diff"," before writing: the plugins added or removed, and the capabilities the composition gains or loses as a union across the bundled set. Read this diff — for a bundled plugin it is the complete list of host operations the package can ever request.",[11,270,271,274,275,277],{},[14,272,273],{},"--dry-run"," prints the diff plus the exact proposed ",[14,276,24],{}," and writes nothing.",[11,279,280,281,284],{},"If the ",[111,282,283],{},"current"," set cannot be loaded — for example the package you are removing is itself broken — the proposed set is still validated and the mutation still proceeds; the diff reports the before-state as unavailable instead of silently showing misleading arithmetic.",[286,287,290],"callout",{"title":288,"type":289},"A package must already be installed","info",[11,291,292,294,295,222,298,301,302,309],{},[14,293,129],{}," only edits the composition list. The package must already be a direct root ",[14,296,297],{},"dependencies",[14,299,300],{},"optionalDependencies"," entry installed from the registry, or the verified loader rejects it: ",[111,303,304,305,308],{},"\"Bundled plugin ",[14,306,307],{},"\u003Cname>"," must be installed as a root dependency or optionalDependency\"",".",[83,311,313],{"id":312},"codegen",[14,314,312],{},[34,316,318],{"className":36,"code":317,"language":38,"meta":39,"style":39},"owlat-plugins codegen                  # regenerate the composition files\nowlat-plugins codegen --check          # fail if output is missing or stale (CI)\nowlat-plugins codegen --boundaries-only  # only enforce the import boundary\n",[14,319,320,330,342],{"__ignoreMap":39},[43,321,322,324,327],{"class":45,"line":46},[43,323,20],{"class":49},[43,325,326],{"class":53}," codegen",[43,328,329],{"class":60},"                  # regenerate the composition files\n",[43,331,332,334,336,339],{"class":45,"line":64},[43,333,20],{"class":49},[43,335,326],{"class":53},[43,337,338],{"class":72}," --check",[43,340,341],{"class":60},"          # fail if output is missing or stale (CI)\n",[43,343,344,346,348,351],{"class":45,"line":254},[43,345,20],{"class":49},[43,347,326],{"class":53},[43,349,350],{"class":72}," --boundaries-only",[43,352,353],{"class":60},"  # only enforce the import boundary\n",[11,355,356,357,360],{},"Regenerates the checked-in Convex and Nuxt composition modules, the Convex component installer, and every contribution catalog\u002Fregistry pair. ",[14,358,359],{},"--boundaries-only"," enforces the rule that core source may not import a configured plugin package outside those generated files — including through Node\u002FBun loaders or repository aliases — without importing plugin code at all.",[11,362,363],{},"Equivalent workspace scripts, which are what CI and the build graph run:",[34,365,367],{"className":36,"code":366,"language":38,"meta":39,"style":39},"bun run plugins:codegen        # regenerate\nbun run plugins:check          # --check\nbun run lint:plugin-imports    # --boundaries-only\n",[14,368,369,381,393],{"__ignoreMap":39},[43,370,371,373,375,378],{"class":45,"line":46},[43,372,50],{"class":49},[43,374,54],{"class":53},[43,376,377],{"class":53}," plugins:codegen",[43,379,380],{"class":60},"        # regenerate\n",[43,382,383,385,387,390],{"class":45,"line":64},[43,384,50],{"class":49},[43,386,54],{"class":53},[43,388,389],{"class":53}," plugins:check",[43,391,392],{"class":60},"          # --check\n",[43,394,395,397,399,402],{"class":45,"line":254},[43,396,50],{"class":49},[43,398,54],{"class":53},[43,400,401],{"class":53}," lint:plugin-imports",[43,403,404],{"class":60},"    # --boundaries-only\n",[11,406,407,410,411,186,414,417,418,421,422,425],{},[14,408,409],{},"bun run plugins:check"," is part of ",[14,412,413],{},"ci:lint",[14,415,416],{},"ci:verify",", and the turbo ",[14,419,420],{},"build"," and ",[14,423,424],{},"deploy"," tasks, so a stale or invalid composition fails the build closed.",[83,427,429],{"id":428},"dev",[14,430,428],{},[34,432,434],{"className":36,"code":433,"language":38,"meta":39,"style":39},"owlat-plugins dev\n",[14,435,436],{"__ignoreMap":39},[43,437,438,440],{"class":45,"line":46},[43,439,20],{"class":49},[43,441,442],{"class":53}," dev\n",[11,444,445,446,448],{},"Regenerates the composition, then re-runs it on every change to ",[14,447,24],{},". Useful while iterating on the bundled set; it is not a substitute for committing the generated output.",[83,450,452],{"id":451},"commit-discipline","Commit discipline",[11,454,455,456,459,460,463],{},"Generated composition files are checked in. Always commit the config edit and the regenerated output ",[97,457,458],{},"together"," — a generated artifact belongs in the same commit as the input that produced it, and ",[14,461,462],{},"plugins:check"," will fail CI otherwise.",[465,466,467],"style",{},"html pre.shiki code .sOLd2, html code.shiki .sOLd2{--shiki-default:#6F42C1;--shiki-dark:#F69D50}html pre.shiki code .s-HuK, html code.shiki .s-HuK{--shiki-default:#032F62;--shiki-dark:#96D0FF}html pre.shiki code .sDN9O, html code.shiki .sDN9O{--shiki-default:#6A737D;--shiki-dark:#768390}html pre.shiki code .sviXB, html code.shiki .sviXB{--shiki-default:#005CC5;--shiki-dark:#6CB6FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}",{"title":39,"searchDepth":64,"depth":64,"links":469},[470,471,472,474,475,476],{"id":85,"depth":64,"text":86},{"id":143,"depth":64,"text":143},{"id":219,"depth":64,"text":473},"add \u002F remove",{"id":312,"depth":64,"text":312},{"id":428,"depth":64,"text":428},{"id":451,"depth":64,"text":452},"Scaffold, add, remove, and regenerate bundled plugin composition with owlat-plugins.","md",{},true,"\u002Fdeveloper\u002Fplugin-cli",{"title":6,"description":477},"3.developer\u002F45.plugin-cli","Ji5hA2lFx14hfauj66Ld3G16lk4xI8bA53NRzlXSLSM",[486,490],{"title":487,"path":488,"stem":489,"children":-1},"Installing & Operating Plugins","\u002Fdeveloper\u002Fplugin-operations","3.developer\u002F44.plugin-operations",{"title":491,"path":492,"stem":493,"children":-1},"Connected Apps & Signed Hooks","\u002Fdeveloper\u002Fplugin-connected-apps","3.developer\u002F46.plugin-connected-apps",1786915092665]