Skip to content
kitn AI/UI

For AI Agents

@kitn.ai/ui ships two auto-generated machine-readable files that follow the llmstxt.org convention. Point your coding agent at them and it will get prop names, event names, and the property-vs-attribute rule right — without hallucinating.

Both files are generated from dist/custom-elements.json by scripts/gen-llms.mjs and committed to the repo, so they are included in every published npm package and always match the installed version.

FileSizeUse
llms.txt~4 KBOrientation: install, the property rule, framework wiring, theming. Paste into a prompt.
llms-full.txt~60 KBEverything in llms.txt plus a generated props/events table for every element, a streaming recipe, and a build runbook.

After npm install @kitn.ai/ui, both files are in the package:

node_modules/@kitn.ai/ui/llms.txt
node_modules/@kitn.ai/ui/llms-full.txt

They are also published at:

ToolWhat to do
Claude CodeAdd @node_modules/@kitn.ai/ui/llms.txt to CLAUDE.md, or run read node_modules/@kitn.ai/ui/llms-full.txt in the session
GitHub CopilotAdd the path to .github/copilot-instructions.md; workspace indexing picks it up
CursorReference the file in .cursorrules
Codex / ChatGPTPaste https://ui.kitn.ai/llms.txt into the prompt, or fetch it with a browsing tool
Any agentnpm install @kitn.ai/ui — file is at node_modules/@kitn.ai/ui/llms.txt

These facts appear in both files. They are the three mistakes that produce silent failures.

1 — Array and object data goes on JS properties, not HTML attributes

Section titled “1 — Array and object data goes on JS properties, not HTML attributes”

An HTML attribute is always a string. Passing messages, models, context, suggestions, or slashCommands as an attribute silently fails.

// Works — set the property in JavaScript
const chat = document.querySelector('kai-chat');
chat.messages = [{ id: '1', role: 'assistant', content: 'Hello!' }];
<!-- Fails — an array can't be an HTML attribute -->
<kai-chat messages="[...]"></kai-chat>

Only scalar props — placeholder, loading, theme — work as attributes.

2 — Events are non-bubbling kai-* CustomEvents

Section titled “2 — Events are non-bubbling kai-* CustomEvents”

Listen directly on the element, not on a parent:

chat.addEventListener('kai-submit', (e) => {
console.log(e.detail.value); // the text the user typed
});

Common events: kai-submit, kai-feedback, kai-model-change, kai-new-chat, kai-select.

3 — Streaming requires a new array and a new object on every chunk

Section titled “3 — Streaming requires a new array and a new object on every chunk”

Mutating an existing message object in place does not trigger a re-render. Replace instead:

// Triggers a re-render on every chunk
chat.messages = chat.messages.map(
(m) => (m.id === assistantId ? { ...m, content: accumulated } : m)
);
// Does NOT trigger a re-render
chat.messages[i].content = accumulated;

The same rule applies to every array/object property: always assign a new reference.

Runbook: wire a streaming chat in 15 lines

Section titled “Runbook: wire a streaming chat in 15 lines”
import '@kitn.ai/ui/elements';
const chat = document.querySelector('kai-chat');
chat.messages = [];
chat.addEventListener('kai-submit', async (e) => {
const userText = e.detail.value;
// Append user message — new array
const history = [
...chat.messages,
{ id: crypto.randomUUID(), role: 'user', content: userText },
];
chat.messages = history;
chat.loading = true;
// Empty assistant placeholder to stream into
const assistantId = crypto.randomUUID();
chat.messages = [...history, { id: assistantId, role: 'assistant', content: '' }];
// Stream — replace with a new array + new object on every chunk
let accumulated = '';
for await (const token of streamFromYourAPI(history)) {
accumulated += token;
chat.messages = chat.messages.map((m) =>
m.id === assistantId ? { ...m, content: accumulated } : m
);
}
chat.loading = false;
});

The raw machine-readable spec — every property, event, attribute, and type — is published at:

https://unpkg.com/@kitn.ai/ui/dist/custom-elements.json

This is the source from which llms.txt and llms-full.txt are generated. IDE plugins that consume the Custom Elements Manifest format can read it directly.