- 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.
Quick start
# 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 start
That 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.
Register the action once. It is a button, a shortcut, a palette entry and an agent tool.
ctx.registerCommand({
id: 'invoices.export',
title: 'invoices.export',
description: 'Export the selected invoices as CSV',
arguments: [
{ name: 'range', kind: 'choice', description: 'The period to export', 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.
What you write is still Angular
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.
Two reasons people pick this up
If you build with an AI assistant
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.
If you want your users to extend it
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.
Everything the user does by hand, your code can do too
// 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 …".
What you would otherwise build twice
- 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.
Three rungs of trust, not one switch
- 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.
Packages
Seven npm packages, one shared version:
How it fits together
- 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
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.
Working in this repo
- 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.
Contributing
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 [email protected], never to a public issue. See
SECURITY.md. Participation is covered by our
Code of Conduct.
Brand
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/.
License
Apache License 2.0, see NOTICE.