Chat pointers are the badged @ mentions you see in HarborClient’s AI sidebar—the same pattern the app uses when you copy a script selection, a response section, or a terminal range into chat. Each badge is compact in the composer; at send time Harbor expands it into an ephemeral system message so the agent sees the captured context.
If you ship a plugin with its own editors, logs, or domain objects, you can register the same kind of pointer and let users pin your context into AI conversations. The Plugin SDK exposes hc.ai.registerChatPointer and hc.ai.copyToChat—including optional custom match and parse when you need a token shape beyond the default @plugin… grammar.
What a plugin pointer looks like
By default, plugin tokens are namespaced under your plugin id:
@plugin.com.example.scripts.script.<key> @plugin.com.example.scripts.script.<key>#12.48

The optional #start.end segment is a character range into the context string you snapped at copy time. With the default grammar, Harbor builds the token when you call copyToChat with pointerId, key, label, and context. With a custom match, you pass the full token yourself.
Register during activate
Declare the ai permission so install confirms the capability. Add ui when you also register toolbar actions or use CopyToChatButton. Chat pointers are runtime-only—no contributes entry is required.
{
"id": "com.example.scripts",
"name": "Example Scripts",
"version": "1.0.0",
"permissions": ["ai", "ui"]
}
Call registerChatPointer once in activate(hc). The id must match [a-z][a-z0-9-]*. Optional agentGuidance is merged into the agent system prompt while your plugin is loaded.
export function activate(hc) {
hc.ai.registerChatPointer({
id: 'script',
agentGuidance:
'When a user message contains @plugin.com.example.scripts.script.<key>, prefer the captured script context in the system message.'
});
}
Registration returns a Disposable. Disposing (or deactivating the plugin) unregisters the pointer and drops live guidance. Badges on past messages still render from persisted reference snapshots.
Custom match and parse
To invent a token shape beyond @plugin.<pluginId>…, supply both match (regex for the body after @) and parse. Patterns that can collide with reserved builtins—plugin, request, res, term, snippet, logs, and the rest—are rejected at registration.
hc.ai.registerChatPointer({
id: 'invoice',
match: /^invoice\.([A-Za-z0-9-]+)(?:#(\d+)\.(\d+))?/,
parse: (match, fullToken, atIndex) => {
const key = match[1];
if (key == null) return null;
return {
key,
selection:
match[2] != null && match[3] != null
? { start: Number(match[2]), end: Number(match[3]) }
: undefined
};
},
agentGuidance: 'When @invoice.<id> appears, use the captured invoice context.'
});
parse returns { key, selection? } or null to reject. It runs in your plugin webview; the host uses a sync fallback for composer highlighting and re-invokes your parse over IPC at copy and send/validate. This is not an expand callback—context is still snapshotted when you call copyToChat.
Copy selection into chat
From your UI—selection toolbar, button, or menu—call hc.ai.copyToChat. Harbor opens the AI sidebar, stores your label and context snapshot, and inserts the badge into the composer.
Default grammar
import { CodeEditor } from '@harborclient/sdk/components';
function ScriptEditor({ scriptUuid, scriptName, source }) {
return (
<CodeEditor
value={source}
selectionToolbarActions={[
{
id: 'copy-to-chat',
onSelect: ({ from, to }) => {
void hc.ai.copyToChat({
pointerId: 'script',
key: scriptUuid,
label: scriptName,
context: source,
selection: { start: from, end: to }
});
}
}
]}
/>
);
}
Custom match
Pass the full token (including @) that matches your registered pattern:
await hc.ai.copyToChat({
pointerId: 'invoice',
token: '@invoice.inv-42#0.12',
label: 'Invoice inv-42',
context: invoiceText,
selection: { start: 0, end: 12 }
});
Register the pointer before you copy—copyToChat validates that pointerId is tracked for your plugin. Context longer than about 100,000 characters is truncated with a clear marker.
Snapshot at copy time
Resolve label and context in your sandbox when the user copies. The host does not call back into your plugin later to expand tokens, so badges keep working even if the original editor is gone. UI selection handlers that call copyToChat run at copy time; custom parse only structures the token—it does not fetch fresh data at send.
- Register in
activate(hc)— one registration per pointer id socopyToChatcan validate. matchandparsetogether — provide both for a custom shape, or neither for the default@plugin…grammar.- Snapshot at copy time — pass full context (and optional selection); no host→plugin expand later.
- Unload-safe history — disposing drops live
agentGuidance; past badges still render from snapshots.
Try it
Walk through the full example in the Chat pointers SDK docs, and see hc.ai for the API reference. Grab the latest HarborClient from harborclient.com or GitHub Releases, load an unpacked plugin with the ai permission, register a pointer, and copy a selection into chat.






Leave a Reply