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. Before 1.0 its breaking changes arrive in minor releases, and the demo application is its reference consumer.
# a fresh Angular app, or the one you have (an Nx workspace works too)
ng new my-studio --style=css --ssr=false && cd my-studio
# the platform, your product and a first plugin, in one go
npx @loomweaver/cli init
npm startThat is the whole list. init installs the packages with the package manager your lockfile names
(npm, pnpm, yarn or bun), scaffolds the distribution and a first weaver, and 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. The weaver is registered 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, running it twice changes nothing, and --dry-run shows the plan.
Getting started shows what you get and what the command wrote. Manual setup is the same application wired by hand, without the service worker and the content-security policy the scaffold adds, and 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. If your tool
reads skills, skills/loomweaver/SKILL.md states the order of work.
{
"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. Or ask it to check what you have: "is every command in this product
something an agent could call, and what would it have to guess at?", and it answers from
validate_commands findings instead of from an impression. Building with an AI
assistant is the path from this block to a running product,
with the server registered in Claude Code, Cursor or VS Code and one run recorded as it happened.
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, so the published contract is the only contract there is, which is the one reason it 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.
Beside it, examples/assistant-workbench is a smaller product built
the same way: a support inbox that a real model operates through the product's own commands, with
your own OpenRouter key. 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.
