Memory extension publishers are the mechanism Remnic uses to install host-specific instruction files into each AI agent host's extension directory. Each publisher knows:
- Where the host stores extensions on disk.
- Whether the host is installed locally.
- How to render and write Remnic-specific instructions.
- How to clean up (unpublish) those artefacts.
This design generalises the pattern previously hard-coded for Codex into a pluggable contract that any host can implement.
Every publisher implements MemoryExtensionPublisher:
interface MemoryExtensionPublisher {
readonly hostId: string;
resolveExtensionRoot(env?: NodeJS.ProcessEnv): Promise<string>;
isHostAvailable(): Promise<boolean>;
renderInstructions(ctx: PublishContext): Promise<string>;
publish(ctx: PublishContext): Promise<PublishResult>;
unpublish(): Promise<void>;
}interface PublishContext {
readonly config: { memoryDir: string; daemonPort?: number; namespace?: string };
readonly skillsRoot: string;
readonly log: { info, warn, error };
}interface PublishResult {
readonly hostId: string;
readonly extensionRoot: string;
readonly filesWritten: string[];
readonly skipped: string[];
}Static capability flags that describe what a publisher can produce:
interface PublisherCapabilities {
readonly instructionsMd: boolean;
readonly skillsFolder: boolean;
readonly citationFormat: boolean;
readonly readPathTemplate: boolean;
}Status: Fully implemented.
- Extension root:
~/.codex/memories_extensions/remnic/ - Writes
instructions.mdwith shared blocks + Codex-specific sandbox rules. - Uses atomic write (temp file + rename) per CLAUDE.md guideline #54.
isHostAvailable()checks for~/.codex/directory existence.
Status: Stub. Claude Code does not yet support file-based memory extension directories. All methods are safe no-ops.
Status: Stub. Hermes uses daemon-based transport. All methods are safe no-ops.
shared-instructions.ts exports four reusable markdown fragments:
| Export | Content |
|---|---|
REMNIC_SEMANTIC_OVERVIEW |
Table of memory types (fact, preference, decision, etc.) |
REMNIC_CITATION_FORMAT |
<oai-mem-citation> block format and examples |
REMNIC_MCP_TOOL_INVENTORY |
Table of all MCP tools the daemon exposes |
REMNIC_RECALL_DECISION_RULES |
When to use MCP recall vs direct file reads |
Each publisher composes these blocks into its host-specific
renderInstructions() output, adding host-specific sections (e.g.
Codex sandbox rules) as needed.
index.ts exports:
const PUBLISHERS: Record<string, () => MemoryExtensionPublisher>;
function publisherFor(hostId: string): MemoryExtensionPublisher | undefined;publisherFor() returns undefined for unknown host IDs rather than
throwing, so callers can gracefully skip unknown hosts.
After a successful connector install, the CLI calls
publisherFor(connectorId) and, if the host is available, publishes
the memory extension automatically.
The doctor command iterates all registered publishers and reports:
- Whether each host is installed locally.
- Whether the extension directory exists.
- Remediation advice if the extension is missing.
- Create
packages/remnic-core/src/memory-extension/<host>-publisher.ts. - Implement
MemoryExtensionPublisher. - Define static
capabilities: PublisherCapabilities. - Register it in
index.ts'sPUBLISHERSmap. - Export the class from
index.ts. - Add it to the main
packages/remnic-core/src/index.tsexports. - Write tests in
tests/memory-extension-publisher.test.ts.