Build Angular workbenches that grow with your product.
An open-source plugin platform for Angular workbenches: an application shell with panes the user splits, a command palette, theming,
plugins that run isolated in their own sandbox, and plugins your users install at runtime. Add tools as your product grows, without touching the shell.
Twenty-seven seconds, no cuts. The rail, the panes, the palette and the status bar are the platform's. Everything inside them can come from plugins, the theme at the end included. Watch it in better quality.
- Admin and operations workbenches
- Internal developer platforms
- Monitoring and data consoles
- Modular business applications, ERP and CRM fronts among them
- Products that want a plugin ecosystem of their own
Who it is for. Angular teams building a product that is a workbench: several things open at once, panes and tabs, a surface other people extend. It is not a component library, and it is not for a site of plain pages. It also speaks AG-UI, the open protocol between a user-facing application and an agentic backend, so every command your product registers can be offered to an agent, and an agent still reaches only what the user could have reached. It is maintained by one person, its API still moves on patch releases before 1.0, and the demo application is its reference consumer.
# 1 · A fresh Angular app (or use the one you have)
ng new my-studio --style=css --ssr=false && cd my-studio
# 2 · Install the platform, then scaffold your product and a first plugin
npm install @loomweaver/shell @loomweaver/plugin-sdk @angular/cdk @jsverse/transloco @ng-icons/heroicons \
@angular/service-worker@$(node -p "require('@angular/core/package.json').version")
npm install -D tailwindcss @tailwindcss/postcss @tailwindcss/typography
npx @loomweaver/cli distribution --name my-studio --title "My Studio" --out . --force
npx @loomweaver/cli weaver --id notes --command --shortcut 'mod+shift+n' --out src/notes
# 3 · Run it
ng serveThat is the whole list. The scaffold also wires your build (the style pipeline, the asset globs that serve the chrome's own strings, the service worker, and the one production setting the generated content-security policy requires) and registers the weaver in your composition root, so its icon is in the rail on the first run. It only ever adds: anything you had already set is left as you set it, and it names every file it touched.
Getting started walks through these steps and what they generate. Manual setup is the same result wired by hand, including the Bootstrap / no-Tailwind path via the pre-compiled stylesheet.
ctx.registerCommand({
id: 'invoices.export',
title: 'invoices.export',
description: 'Export the selected invoices as CSV',
arguments: [
{ name: 'range', kind: 'choice', choices: ['month', 'quarter', 'year'], required: true },
],
callable: true,
run: (_context, args) => exportInvoices(String(args?.['range'])),
});The rail item, the keystroke, the context menu and the command palette already point at the same
command. callable: true adds one more caller: an agent speaking AG-UI,
an open standard we implement rather than one we invented. You never keep a second list of tools
beside the first, and the guarantee holds without you writing a line of it: an agent reaches what
the user could have reached, and nothing more.
See Callable commands and Agent tools.
You write your domain UI as plugins ("weavers") against one small contract and compose them into
a branded distribution, mostly one providers array. A weaver is a manifest and one activate(),
and what it hands the workbench are ordinary standalone components against the router you already
use. One surface can own your whole route tree, so an app you already have moves in behind a single
plugin; you split it into several when you want two documents side by side.
Every new project means teaching it your structure, your routing and your conventions all over again. You write another instructions file, and it still invents things. The UI is exactly where it invents most.
LoomWeaver is the same workbench every time, and it is written down for machines:
llms.txt as the map, llms-full.txt as the whole contract in a single
fetch, and @loomweaver/mcp so your assistant scaffolds with tools instead of guesses.
{
"mcpServers": {
"loomweaver": { "command": "npx", "args": ["-y", "@loomweaver/mcp"] }
}
}Then just ask for it: "add a weaver called invoices with a command and a settings section". The
same generators run three ways, so it does not matter whether a person, a CLI or an assistant
invokes them: @loomweaver/cli from any command line, @loomweaver/devkit as Nx generators, and
@loomweaver/mcp over MCP.
You built a good tool. People have ideas, and some of them would build those ideas themselves if they could. They can't, because there is no way in, and opening one means isolation, permissions, a store, updates and an API you promise not to break. That is not a feature, that is half a year.
All of it is here. And there is no privileged host API: your own product UI goes through the exact same door a stranger's plugin does, which is the only reason a published contract does not quietly rot. Your community can do everything you can do.
Every tool with a living ecosystem got there the same way. The plugins made the product, not the roadmap.
And the two feed each other. Somebody who wants to contribute to your tool points their own
assistant at your llms-full.txt and starts. Your contributor onboarding is a URL.
Any AG-UI agent can drive your product. It still cannot reach further than the person at the keyboard.
AG-UI is the open protocol between a user-facing application and an agentic backend, and it is not ours. LoomWeaver ships the adapter, so a backend that already speaks it drives your product with nothing in between. You adopt a standard, not a mechanism of ours, and the thing on the other end can be swapped for another implementation of it.
Generate a weaver with --agent and you get the whole path on this side: a docked panel, the seam
that decides about a call before it runs, and a local stand-in that speaks the protocol so it works
on the first serve. No transport, no key and no model are generated, because those are yours.
Every call goes through the same seam a button, a shortcut and the palette already go through, so an agent inherits the permissions and the access gating that were already there. A refusal reads the same whatever its reason, so nothing can be learned about what is installed by asking for it. Say no, and the workbench is never asked.
See Driving your product with an AG-UI agent, or open the live demo and decline a call yourself.
// your product's own toolbar, not a plugin
export class Toolbar {
private readonly panes = inject(PaneService);
private readonly switches = inject(FeatureSwitches);
constructor() {
this.switches.update({ content: { splitRight: false } }); // hide our split button …
}
protected split(): void {
this.panes.splitRight(); // … and offer the same action from yours
}
}Every pane, workspace, sidebar and switch the workbench offers is also a service your own code injects. Turn a built-in control off and offer the action from your own toolbar, menu or admin page. The service runs the same code the control runs, asks the same question about unsaved work, and keeps working when the control is gone. A switch removes the control, never the capability. The Distribution API is indexed by "I want to …".
- A real workspace with tab groups, drag-to-split panes, pop-out windows, named
workspaces, a command palette (
⌘K), quick-open (⌘P), preview tabs and pinning. - Theming from semantic tokens (plain CSS variables). Use the pre-compiled stylesheet with Bootstrap or no framework at all, or bring Tailwind. Precedence is product < plugin < tenant.
- Auth-aware chrome. Contributions declare an
accessrequirement and the shell hides, disables or blocks them reactively. Your product brings the session, from whatever auth you already run. - No server in the platform. Settings, working state and auth are frontend ports with local defaults. Wire them to your own backend (any stack) or run fully standalone.
- AG-UI already spoken. Every command you register can be offered to an agent that speaks the standard, through an adapter that ships with the platform. You write no dispatch and keep no second list of tools, and the agent still reaches only what the user could have reached.
- The rest: an installable PWA with an update flow, i18n with namespaced composition, WCAG 2.1 AA accessibility, cross-window state sync, and save/discard/cancel for editors with unsaved work.
- Trusted, in-process: your own weavers, composed at build time.
- Sandboxed iframe: somebody else's code in its own JS context, opaque origin, no reach into your DOM. Write the plugin body in any framework.
- Installed at runtime: from your curated catalogue, with a consent dialog the user answers and updates driven by the catalogue version.
All three consume the same ctx, behind a default-deny capability broker the user can inspect
and revoke. Moving a plugin down a rung is a change of trust, not a rewrite.
Seven npm packages, one shared version:
| Package | What it is |
|---|---|
@loomweaver/plugin-sdk |
the plugin contract (what a weaver imports) |
@loomweaver/shell |
the neutral host chrome (Angular) |
@loomweaver/frame-kit |
UI assets for sandboxed (iframe) plugins |
@loomweaver/cli |
scaffolding from the command line |
@loomweaver/devkit |
the same scaffolds as Nx generators |
@loomweaver/mcp |
the same scaffolds over MCP, for AI assistants |
@loomweaver/ag-ui |
the AG-UI adapter: the workbench's commands, offered to an agent |
- Layer 1, LoomWeaver (this repo): plugin registry, extension points, capability broker, theming engine, sandbox RPC, plugin loader. Domain-pure, frontend-only.
- Layer 2, your weavers: the product UI, mechanically indistinguishable from third-party plugins.
- A product is a distribution: a thin composition of the published packages, and it never forks the core. See Building a distribution and Backend integration for the product hand-off.
Read Architecture for the mental model, and Samples for copyable recipes: views, commands, dialogs, settings, sync.
The live demo is a product, not a sample app. It sits beside the
platform in demo/ as its own Angular workspace and installs the published @loomweaver/* packages
from the registry exactly as any other consumer would, so nothing it shows depends on being next to
the source. Every merge to main that touches it deploys it.
It carries quotes and their documents, a dashboard, an agent driving the workbench through its own commands, a sandboxed payment matcher, several visibly different themes and access-gated content. Getting started gets you your own product in five minutes.
- This is where LoomWeaver is developed.
mainis protected and takes no direct pushes; work arrives through pull requests that have to pass the checks first. The history before the first public commit is not here: the project was developed privately and opened at a fixed state. - Frontend: Angular + Nx under
platform/, and this repo is frontend-only. Running the testbed takes a certificate you trust once and a port that answers on IPv4;CONTRIBUTING.mdwalks through it. - Documentation source:
docs/, the same content published at loomweaver.dev. Start at the docs index. - AI-facing map (for integrators and their assistants):
llms.txt+llms-full.txt, a curated entry point and a single-fetch full brief.
Issues are the main channel: bug reports, questions and proposals are all welcome. Small,
self-contained pull requests are welcome too; for anything larger, please open an issue first so the
design conversation happens before you spend an evening on it.
CONTRIBUTING.md has the DCO sign-off, the development setup and the code
conventions.
Security reports go to security@loomweaver.dev, never to a public issue. See
SECURITY.md. Participation is covered by our
Code of Conduct.
The mark is a woven mat: blue warp threads with gold accent threads woven through (the plugins).
Colours: blue #2E96C9, gold #C59A2F. Assets in assets/brand/.
Apache License 2.0, see NOTICE.
