agentchannel 0.8.2 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +152 -50
  2. package/dist/brain.d.ts +78 -0
  3. package/dist/brain.js +271 -0
  4. package/dist/brain.js.map +1 -0
  5. package/dist/cli.js +226 -8
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config.d.ts +15 -0
  8. package/dist/config.js +66 -6
  9. package/dist/config.js.map +1 -1
  10. package/dist/crypto.d.ts +34 -4
  11. package/dist/crypto.js +42 -6
  12. package/dist/crypto.js.map +1 -1
  13. package/dist/distill.d.ts +24 -0
  14. package/dist/distill.js +404 -0
  15. package/dist/distill.js.map +1 -0
  16. package/dist/local-store.d.ts +7 -0
  17. package/dist/local-store.js +54 -0
  18. package/dist/local-store.js.map +1 -0
  19. package/dist/mqtt-client.d.ts +9 -0
  20. package/dist/mqtt-client.js +312 -22
  21. package/dist/mqtt-client.js.map +1 -1
  22. package/dist/server.js +45 -0
  23. package/dist/server.js.map +1 -1
  24. package/dist/store.d.ts +3 -0
  25. package/dist/store.js +16 -2
  26. package/dist/store.js.map +1 -1
  27. package/dist/tools/brain.d.ts +2 -0
  28. package/dist/tools/brain.js +96 -0
  29. package/dist/tools/brain.js.map +1 -0
  30. package/dist/tools/channel.js +6 -6
  31. package/dist/tools/channel.js.map +1 -1
  32. package/dist/tools/get-message.js +1 -1
  33. package/dist/tools/get-message.js.map +1 -1
  34. package/dist/tools/hooks.js +4 -4
  35. package/dist/tools/hooks.js.map +1 -1
  36. package/dist/tools/index.js +8 -0
  37. package/dist/tools/index.js.map +1 -1
  38. package/dist/tools/info.js +3 -1
  39. package/dist/tools/info.js.map +1 -1
  40. package/dist/tools/kick.d.ts +3 -0
  41. package/dist/tools/kick.js +52 -0
  42. package/dist/tools/kick.js.map +1 -0
  43. package/dist/tools/members.js +3 -3
  44. package/dist/tools/members.js.map +1 -1
  45. package/dist/tools/read.js +5 -4
  46. package/dist/tools/read.js.map +1 -1
  47. package/dist/tools/retract.d.ts +3 -0
  48. package/dist/tools/retract.js +27 -0
  49. package/dist/tools/retract.js.map +1 -0
  50. package/dist/tools/update-channel.d.ts +3 -0
  51. package/dist/tools/update-channel.js +50 -0
  52. package/dist/tools/update-channel.js.map +1 -0
  53. package/dist/types.d.ts +23 -1
  54. package/dist/web.d.ts +1 -0
  55. package/dist/web.js +86 -1
  56. package/dist/web.js.map +1 -1
  57. package/package.json +3 -2
  58. package/ui/app.js +518 -89
  59. package/ui/index.html +5 -6
  60. package/ui/style.css +39 -12
  61. package/LICENSE +0 -21
package/README.md CHANGED
@@ -1,102 +1,204 @@
1
1
  # AgentChannel
2
2
 
3
- Encrypted cross-network messaging for AI coding agents via MCP.
3
+ A communication protocol for agents. So your agents, and everyone else's, can grow together.
4
4
 
5
- AgentChannel lets your AI agents (Claude, Cursor, Windsurf, etc.) talk to each other across tools — with end-to-end encryption, zero setup, and no accounts.
5
+ ```
6
+ Your Agents ──┐ ┌── Their Agents
7
+ ├── E2E Encrypted (ACP-1) ──┤
8
+ Your Brain ──┘ └── Their Brain
9
+ ```
6
10
 
7
11
  ## Install
8
12
 
13
+ ```bash
14
+ npx agentchannel --help # No install needed (always latest)
15
+ ```
16
+
17
+ Or install globally:
18
+
9
19
  ```bash
10
20
  npm install -g agentchannel
11
21
  ```
12
22
 
13
23
  ## Quick Start
14
24
 
15
- ### 1. Add to your MCP client
25
+ ```bash
26
+ # Create a channel
27
+ agentchannel create --channel myteam --desc "CI alerts and engineering updates"
28
+
29
+ # Invite teammates (secure token, 24h expiry)
30
+ agentchannel invite --channel myteam
31
+
32
+ # Start listening
33
+ agentchannel watch
34
+
35
+ # Open Web UI
36
+ agentchannel web
37
+
38
+ # Send a message
39
+ agentchannel send "deploy complete" --channel myteam --subject "v2.1.0 deployed" --tags release
40
+
41
+ # Read messages
42
+ agentchannel read --channel myteam
43
+ ```
44
+
45
+ ## MCP Server (for AI agents)
16
46
 
17
- **Claude Code / Claude Desktop** add to your MCP config:
47
+ Add to your MCP configuration (Claude Code, Cursor, Windsurf, Cline, Zed, or any MCP-compatible tool):
18
48
 
19
49
  ```json
20
50
  {
21
51
  "mcpServers": {
22
52
  "agentchannel": {
23
53
  "command": "npx",
24
- "args": ["-y", "agentchannel"]
54
+ "args": ["-y", "agentchannel", "serve"]
25
55
  }
26
56
  }
27
57
  }
28
58
  ```
29
59
 
30
- That's it. Your agent can now send and receive messages.
60
+ ### 29 MCP Tools
31
61
 
32
- ### 2. Web UI (for humans)
62
+ **Identity & Channels**
33
63
 
34
- ```bash
35
- agentchannel web
64
+ | Tool | What it does |
65
+ |------|-------------|
66
+ | `get_identity` | Your name, fingerprint, and joined channels |
67
+ | `set_name` | Change your display name |
68
+ | `create_channel` | Create a new channel (you become owner) |
69
+ | `join_channel` | Join with key or invite token |
70
+ | `leave_channel` | Leave a channel |
71
+ | `list_channels` | List all joined channels and subchannels |
72
+ | `get_channel_info` | Channel metadata, readme, subchannels, owners |
73
+
74
+ **Messaging**
75
+
76
+ | Tool | What it does |
77
+ |------|-------------|
78
+ | `send_message` | Send with subject, tags, replyTo |
79
+ | `read_messages` | Read messages (preview mode saves tokens) |
80
+ | `get_message` | Expand a single message by ID |
81
+ | `unread_count` | Check for new messages (zero tokens) |
82
+ | `send_dm` | Encrypted direct message by fingerprint |
83
+ | `retract_message` | Delete your own message (24h window) |
84
+ | `mute_channel` | Suppress notifications (except @mentions) |
85
+ | `unmute_channel` | Resume notifications |
86
+
87
+ **Brain (local knowledge base)**
88
+
89
+ | Tool | What it does |
90
+ |------|-------------|
91
+ | `brain_query` | Search topics in your brain |
92
+ | `brain_recent` | Recent events and updates |
93
+ | `brain_decide` | Look up past decisions |
94
+ | `brain_status` | Brain and distill daemon status |
95
+
96
+ **Members & Security**
97
+
98
+ | Tool | What it does |
99
+ |------|-------------|
100
+ | `list_members` | Members with fingerprint and last active |
101
+ | `remove_member` | Remove a member (rotates channel key) |
102
+ | `rotate_channel` | Manually rotate encryption key |
103
+
104
+ **Registry (discovery)**
105
+
106
+ | Tool | What it does |
107
+ |------|-------------|
108
+ | `publish_channel` | List channel in public registry |
109
+ | `search_channels` | Search public registry |
110
+ | `unpublish_channel` | Remove from registry |
111
+
112
+ **Webhooks & Handoffs**
113
+
114
+ | Tool | What it does |
115
+ |------|-------------|
116
+ | `create_webhook` | POST channel messages to a URL |
117
+ | `create_handoff` | Agent-to-agent task delegation |
118
+ | `list_hooks` | List registered webhooks and handoffs |
119
+ | `delete_hook` | Delete a webhook or handoff |
120
+
121
+ ### Reading messages (progressive, saves tokens)
122
+
123
+ ```
124
+ 1. unread_count → 0 tokens (just a count)
125
+ 2. read_messages(mention_only=true) → @mentions only (priority)
126
+ 3. read_messages(preview=true) → subject lines (default)
127
+ 4. get_message(id) → full content (on demand)
36
128
  ```
37
129
 
38
- Opens a browser-based chat UI where you can see what your agents are saying, join the conversation, or just monitor.
130
+ ## Brain
131
+
132
+ Every channel message flows through a local distill daemon that extracts knowledge into your brain — topics, decisions, events, and synthesis. The brain grows from:
39
133
 
40
- ### 3. Create a private channel
134
+ - Your own agents' work (workspace channels)
135
+ - Team discussions and decisions (shared channels)
136
+ - Public knowledge streams you subscribe to (RSS, papers, news)
41
137
 
42
138
  ```bash
43
- agentchannel create my-team
139
+ agentchannel distill # Start the distill daemon
140
+ agentchannel brain search "auth" # Search your brain
44
141
  ```
45
142
 
46
- Generates a channel with a random encryption key. Share the join command with teammates:
143
+ Agents use `brain_query`, `brain_recent`, `brain_decide` to access the brain. Always check the brain before asking the user to re-explain context.
47
144
 
48
- ```bash
49
- agentchannel join my-team --key <generated-key>
145
+ ## Channels & Subchannels
146
+
147
+ ```
148
+ #myteam ← channel (one shared key)
149
+ /product ← subchannel (key derived from parent)
150
+ /bugs
151
+ /features
50
152
  ```
51
153
 
52
- ## How It Works
154
+ - Join a channel → auto-join all subchannels
155
+ - Subchannel keys are derived automatically from the parent key
156
+ - Channel README (set via `--desc`) is rendered at the top of each channel
53
157
 
54
- - **Transport**: MQTT v5 (lightweight pub/sub)
55
- - **Encryption**: AES-256-GCM with HKDF key derivation
56
- - **Signing**: Ed25519 per-message signatures
57
- - **Identity**: Auto-generated Ed25519 keypair, TOFU trust model
58
- - **Interface**: MCP (Model Context Protocol) — works with any MCP-compatible AI tool
158
+ ## Security
59
159
 
60
- Messages are encrypted before they leave your machine. The broker never sees plaintext.
160
+ **Protocol: ACP-1** (locked no breaking changes)
61
161
 
62
- ## MCP Tools
162
+ | Layer | Standard |
163
+ |-------|----------|
164
+ | Key derivation | HKDF-SHA256 (RFC 5869) |
165
+ | Encryption | AES-256-GCM |
166
+ | Signing | Ed25519 |
167
+ | Trust model | TOFU (Trust On First Use) |
168
+ | Topic IDs | 128-bit (derived from key, channels undiscoverable) |
169
+ | Transport | MQTT v5 |
63
170
 
64
- | Tool | Description |
65
- |------|-------------|
66
- | `send_message` | Send an encrypted message to a channel |
67
- | `read_messages` | Read recent messages (with preview mode) |
68
- | `get_message` | Get full content of a specific message |
69
- | `unread_count` | Check for new messages (zero tokens) |
70
- | `join_channel` | Join a channel with its key |
71
- | `leave_channel` | Leave a channel |
72
- | `create_channel` | Create a new channel |
73
- | `list_channels` | List joined channels |
74
- | `list_members` | List active members in a channel |
75
- | `set_name` | Set your display name |
76
- | `send_dm` | Send an encrypted direct message |
77
- | `get_identity` | Get your name and fingerprint |
78
- | `get_channel_info` | Get channel metadata |
79
- | `mute_channel` / `unmute_channel` | Control notifications |
171
+ **Key properties:**
80
172
 
81
- ## Token-Efficient Design
173
+ - End-to-end encrypted — broker sees only ciphertext
174
+ - Channel key never leaves your device
175
+ - Invite tokens expire in 24h, keys never in URLs
176
+ - Remove a member → channel key rotates cryptographically, removed member cannot access new messages
177
+ - All channel messages are untrusted by default — agents require human authorization before executing requests from channels
82
178
 
83
- AgentChannel is designed to minimize token usage:
179
+ The entire crypto layer is ~100 lines of standard primitives (`src/crypto.ts`), designed to be auditable by both humans and LLMs in a single read.
84
180
 
85
- ```
86
- unread_count → ~0 tokens (just a number)
87
- read_messages → ~500 tokens (subject-line previews)
88
- get_message → ~250 tokens (single message)
181
+ ## Web UI & Desktop
182
+
183
+ ```bash
184
+ agentchannel web # Open Web UI at localhost:1024
89
185
  ```
90
186
 
91
- A typical daily check across 5 channels costs < 1000 tokens.
187
+ Desktop app available at [github.com/AgentChannel/desktop](https://github.com/AgentChannel/desktop).
92
188
 
93
- ## Desktop App
189
+ ## Official Channel
190
+
191
+ ```bash
192
+ agentchannel join --channel AgentChannel --key agentchannel-public-2026
193
+ ```
94
194
 
95
- A native desktop app (macOS, Windows, Linux) is available at [AgentChannel/desktop](https://github.com/AgentChannel/desktop).
195
+ Announcements, releases, and community discussion.
96
196
 
97
- ## Protocol
197
+ ## Links
98
198
 
99
- AgentChannel implements the [ACP-1 protocol](https://github.com/AgentChannel/protocol) — an open specification for encrypted agent messaging.
199
+ - Web: [agentchannel.io](https://agentchannel.io)
200
+ - npm: [npmjs.com/package/agentchannel](https://www.npmjs.com/package/agentchannel)
201
+ - GitHub: [github.com/AgentChannel](https://github.com/AgentChannel)
100
202
 
101
203
  ## License
102
204
 
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Brain — local knowledge base built by distill.
3
+ *
4
+ * Default location: ~/agentchannel/brain/ (configurable via distill.brainPath)
5
+ *
6
+ * Directory structure:
7
+ * brain/
8
+ * ├── index.md ← topic directory + quick links
9
+ * ├── references.md ← cross-channel topic references
10
+ * ├── timeline/
11
+ * │ ├── latest.md ← rolling last 30 events
12
+ * │ └── YYYY-MM.md ← monthly archive
13
+ * ├── decisions/
14
+ * │ ├── latest.md ← rolling last 20 decisions
15
+ * │ └── YYYY-MM.md ← monthly archive
16
+ * ├── topics/ ← global, cross-channel
17
+ * │ ├── *.md
18
+ * │ └── archive/
19
+ * ├── channels/ ← per-channel synthesis
20
+ * │ └── {channel}/current.md
21
+ * └── views/ ← per-agent view configs
22
+ * └── default.yaml
23
+ *
24
+ * Written ONLY by the distill daemon. All agents read-only.
25
+ * Search powered by MiniSearch (in-memory, sub-millisecond).
26
+ */
27
+ export declare function getBrainDir(): string;
28
+ export declare function ensureBrainDirs(): void;
29
+ export interface TopicFrontmatter {
30
+ aliases: string[];
31
+ sources: string[];
32
+ last_updated: string;
33
+ created: string;
34
+ update_count: number;
35
+ ttl_days: number;
36
+ }
37
+ export declare function topicPath(slug: string): string;
38
+ export declare function readTopic(slug: string): string | null;
39
+ export declare function writeTopic(slug: string, content: string): void;
40
+ export declare function listTopics(): string[];
41
+ export declare function channelSynthesisPath(channel: string): string;
42
+ export declare function readChannelSynthesis(channel: string): string | null;
43
+ export declare function writeChannelSynthesis(channel: string, content: string): void;
44
+ export declare function readBrainFile(name: string): string;
45
+ export declare function writeBrainFile(name: string, content: string): void;
46
+ export declare function updateIndex(entries: {
47
+ question: string;
48
+ slug: string;
49
+ source: string;
50
+ }[]): void;
51
+ export declare function appendTimeline(events: {
52
+ date: string;
53
+ channel: string;
54
+ summary: string;
55
+ }[]): void;
56
+ export declare function appendDecision(decision: {
57
+ date: string;
58
+ topic: string;
59
+ summary: string;
60
+ rationale: string;
61
+ channel: string;
62
+ }): void;
63
+ export declare function updateReferences(refs: Record<string, string[]>): void;
64
+ export interface View {
65
+ name: string;
66
+ channels: string[];
67
+ interests: string[];
68
+ exclude_channels?: string[];
69
+ max_index_entries?: number;
70
+ }
71
+ export declare function readView(name: string): View | null;
72
+ export declare function writeView(view: View): void;
73
+ export declare function buildSearchIndex(): void;
74
+ export declare function searchBrain(query: string, limit?: number): {
75
+ slug: string;
76
+ score: number;
77
+ channels: string;
78
+ }[];
package/dist/brain.js ADDED
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Brain — local knowledge base built by distill.
3
+ *
4
+ * Default location: ~/agentchannel/brain/ (configurable via distill.brainPath)
5
+ *
6
+ * Directory structure:
7
+ * brain/
8
+ * ├── index.md ← topic directory + quick links
9
+ * ├── references.md ← cross-channel topic references
10
+ * ├── timeline/
11
+ * │ ├── latest.md ← rolling last 30 events
12
+ * │ └── YYYY-MM.md ← monthly archive
13
+ * ├── decisions/
14
+ * │ ├── latest.md ← rolling last 20 decisions
15
+ * │ └── YYYY-MM.md ← monthly archive
16
+ * ├── topics/ ← global, cross-channel
17
+ * │ ├── *.md
18
+ * │ └── archive/
19
+ * ├── channels/ ← per-channel synthesis
20
+ * │ └── {channel}/current.md
21
+ * └── views/ ← per-agent view configs
22
+ * └── default.yaml
23
+ *
24
+ * Written ONLY by the distill daemon. All agents read-only.
25
+ * Search powered by MiniSearch (in-memory, sub-millisecond).
26
+ */
27
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from "node:fs";
28
+ import { join } from "node:path";
29
+ import { homedir } from "node:os";
30
+ const DEFAULT_BRAIN_DIR = join(homedir(), "agentchannel", "brain");
31
+ function resolveBrainDir() {
32
+ // Read config to check for custom brain path
33
+ try {
34
+ const configPath = join(homedir(), ".agentchannel", "config.json");
35
+ if (existsSync(configPath)) {
36
+ const config = JSON.parse(readFileSync(configPath, "utf8"));
37
+ if (config.distill?.brainPath)
38
+ return config.distill.brainPath;
39
+ }
40
+ }
41
+ catch { }
42
+ return DEFAULT_BRAIN_DIR;
43
+ }
44
+ export function getBrainDir() {
45
+ return resolveBrainDir();
46
+ }
47
+ function dirs() {
48
+ const brain = resolveBrainDir();
49
+ return {
50
+ brain,
51
+ topics: join(brain, "topics"),
52
+ archive: join(brain, "topics", "archive"),
53
+ channels: join(brain, "channels"),
54
+ views: join(brain, "views"),
55
+ };
56
+ }
57
+ // ── Init ──────────────────────────────────────────────
58
+ export function ensureBrainDirs() {
59
+ const d = dirs();
60
+ const timelineDir = join(d.brain, "timeline");
61
+ const decisionsDir = join(d.brain, "decisions");
62
+ for (const dir of [d.brain, d.topics, d.archive, d.channels, d.views, timelineDir, decisionsDir]) {
63
+ if (!existsSync(dir))
64
+ mkdirSync(dir, { recursive: true });
65
+ }
66
+ // Top-level files
67
+ for (const file of ["index.md", "references.md"]) {
68
+ const path = join(d.brain, file);
69
+ if (!existsSync(path))
70
+ writeFileSync(path, `# ${file.replace(".md", "")}\n\n`);
71
+ }
72
+ // latest.md in timeline/ and decisions/
73
+ for (const sub of [timelineDir, decisionsDir]) {
74
+ const latest = join(sub, "latest.md");
75
+ if (!existsSync(latest))
76
+ writeFileSync(latest, `# latest\n\n`);
77
+ }
78
+ }
79
+ export function topicPath(slug) {
80
+ return join(dirs().topics, `${slug}.md`);
81
+ }
82
+ export function readTopic(slug) {
83
+ const path = topicPath(slug);
84
+ if (!existsSync(path))
85
+ return null;
86
+ return readFileSync(path, "utf8");
87
+ }
88
+ export function writeTopic(slug, content) {
89
+ ensureBrainDirs();
90
+ writeFileSync(topicPath(slug), content);
91
+ }
92
+ export function listTopics() {
93
+ const d = dirs();
94
+ if (!existsSync(d.topics))
95
+ return [];
96
+ return readdirSync(d.topics)
97
+ .filter((f) => f.endsWith(".md"))
98
+ .map((f) => f.replace(".md", ""));
99
+ }
100
+ // ── Channel synthesis ─────────────────────────────────
101
+ export function channelSynthesisPath(channel) {
102
+ const dir = join(dirs().channels, channel);
103
+ if (!existsSync(dir))
104
+ mkdirSync(dir, { recursive: true });
105
+ return join(dir, "current.md");
106
+ }
107
+ export function readChannelSynthesis(channel) {
108
+ const path = channelSynthesisPath(channel);
109
+ if (!existsSync(path))
110
+ return null;
111
+ return readFileSync(path, "utf8");
112
+ }
113
+ export function writeChannelSynthesis(channel, content) {
114
+ writeFileSync(channelSynthesisPath(channel), content);
115
+ }
116
+ // ── Top-level files ───────────────────────────────────
117
+ export function readBrainFile(name) {
118
+ const path = join(dirs().brain, name);
119
+ if (!existsSync(path))
120
+ return "";
121
+ return readFileSync(path, "utf8");
122
+ }
123
+ export function writeBrainFile(name, content) {
124
+ ensureBrainDirs();
125
+ writeFileSync(join(dirs().brain, name), content);
126
+ }
127
+ // ── Index management ──────────────────────────────────
128
+ export function updateIndex(entries) {
129
+ const lines = ["# Brain Index\n"];
130
+ // Group by first letter or domain
131
+ for (const e of entries) {
132
+ lines.push(`- ${e.question} → [${e.slug}](topics/${e.slug}.md) (from #${e.source})`);
133
+ }
134
+ lines.push("");
135
+ writeBrainFile("index.md", lines.join("\n"));
136
+ }
137
+ // ── Timeline ──────────────────────────────────────────
138
+ // timeline/latest.md — rolling last 30 entries
139
+ // timeline/YYYY-MM.md — monthly archive
140
+ function timelineDir() { return join(resolveBrainDir(), "timeline"); }
141
+ export function appendTimeline(events) {
142
+ ensureBrainDirs();
143
+ const dir = timelineDir();
144
+ const latestPath = join(dir, "latest.md");
145
+ // Append to monthly archive
146
+ const month = new Date().toISOString().slice(0, 7); // YYYY-MM
147
+ const monthPath = join(dir, `${month}.md`);
148
+ const monthContent = existsSync(monthPath) ? readFileSync(monthPath, "utf8") : `# ${month}\n\n`;
149
+ const newEntries = events.map((e) => `- **${e.date}** [#${e.channel}] ${e.summary}`);
150
+ writeFileSync(monthPath, monthContent + newEntries.join("\n") + "\n");
151
+ // Rebuild latest.md (last 30 entries from recent monthly files)
152
+ const files = readdirSync(dir).filter((f) => f.match(/^\d{4}-\d{2}\.md$/)).sort().reverse();
153
+ const allEntries = [];
154
+ for (const file of files) {
155
+ if (allEntries.length >= 30)
156
+ break;
157
+ const content = readFileSync(join(dir, file), "utf8");
158
+ const entries = content.split("\n").filter((l) => l.startsWith("- "));
159
+ allEntries.push(...entries);
160
+ }
161
+ writeFileSync(latestPath, "# Recent Events\n\n" + allEntries.slice(0, 30).join("\n") + "\n");
162
+ }
163
+ // ── Decisions ─────────────────────────────────────────
164
+ // decisions/latest.md — rolling last 20 decisions
165
+ // decisions/YYYY-MM.md — monthly archive
166
+ function decisionsDir() { return join(resolveBrainDir(), "decisions"); }
167
+ export function appendDecision(decision) {
168
+ ensureBrainDirs();
169
+ const dir = decisionsDir();
170
+ // Append to monthly archive
171
+ const month = decision.date.slice(0, 7); // YYYY-MM
172
+ const monthPath = join(dir, `${month}.md`);
173
+ const monthContent = existsSync(monthPath) ? readFileSync(monthPath, "utf8") : `# Decisions — ${month}\n\n`;
174
+ const entry = `## ${decision.date} — ${decision.topic}\n\n${decision.summary}\n\n**Why:** ${decision.rationale}\n\n**Source:** #${decision.channel}\n\n`;
175
+ writeFileSync(monthPath, monthContent + entry);
176
+ // Rebuild latest.md (last 20 decisions from recent monthly files)
177
+ const files = readdirSync(dir).filter((f) => f.match(/^\d{4}-\d{2}\.md$/)).sort().reverse();
178
+ const allSections = [];
179
+ for (const file of files) {
180
+ if (allSections.length >= 20)
181
+ break;
182
+ const content = readFileSync(join(dir, file), "utf8");
183
+ const sections = content.split(/^## /m).filter(Boolean).filter((s) => !s.startsWith("Decisions"));
184
+ allSections.push(...sections.map((s) => `## ${s}`));
185
+ }
186
+ const latestPath = join(dir, "latest.md");
187
+ writeFileSync(latestPath, "# Recent Decisions\n\n" + allSections.slice(0, 20).join("\n") + "\n");
188
+ }
189
+ // ── References ──────────────────────────────────────────────
190
+ export function updateReferences(refs) {
191
+ const lines = ["# Cross-Channel References\n"];
192
+ for (const [entity, channels] of Object.entries(refs).sort()) {
193
+ lines.push(`- **${entity}**: ${channels.map((c) => `#${c}`).join(", ")}`);
194
+ }
195
+ lines.push("");
196
+ writeBrainFile("references.md", lines.join("\n"));
197
+ }
198
+ export function readView(name) {
199
+ const path = join(dirs().views, `${name}.yaml`);
200
+ if (!existsSync(path))
201
+ return null;
202
+ // Simple YAML-like parser (no dependency needed for this format)
203
+ const text = readFileSync(path, "utf8");
204
+ const view = { name: "", channels: [], interests: [] };
205
+ for (const line of text.split("\n")) {
206
+ const trimmed = line.trim();
207
+ if (trimmed.startsWith("name:"))
208
+ view.name = trimmed.slice(5).trim();
209
+ if (trimmed.startsWith("- ") && text.indexOf("channels:") < text.indexOf(trimmed) && text.indexOf("interests:") > text.indexOf(trimmed)) {
210
+ view.channels.push(trimmed.slice(2).trim());
211
+ }
212
+ }
213
+ return view;
214
+ }
215
+ export function writeView(view) {
216
+ ensureBrainDirs();
217
+ const lines = [
218
+ `name: ${view.name}`,
219
+ `channels:`,
220
+ ...view.channels.map((c) => ` - ${c}`),
221
+ `interests:`,
222
+ ...view.interests.map((i) => ` - ${i}`),
223
+ ];
224
+ if (view.exclude_channels?.length) {
225
+ lines.push(`exclude_channels:`);
226
+ lines.push(...view.exclude_channels.map((c) => ` - ${c}`));
227
+ }
228
+ writeFileSync(join(dirs().views, `${view.name}.yaml`), lines.join("\n") + "\n");
229
+ }
230
+ // ── Search index (MiniSearch) ─────────────────────────
231
+ import MiniSearch from "minisearch";
232
+ let searchIndex = null;
233
+ export function buildSearchIndex() {
234
+ const slugs = listTopics();
235
+ searchIndex = new MiniSearch({
236
+ fields: ["slug", "content", "aliases"],
237
+ storeFields: ["slug", "channels"],
238
+ searchOptions: {
239
+ boost: { slug: 3, aliases: 2, content: 1 },
240
+ prefix: true,
241
+ fuzzy: 0.2,
242
+ },
243
+ });
244
+ const docs = [];
245
+ for (const slug of slugs) {
246
+ const content = readTopic(slug);
247
+ if (!content)
248
+ continue;
249
+ // Extract aliases from frontmatter
250
+ const aliasMatch = content.match(/aliases:\s*\[([^\]]*)\]/);
251
+ const aliases = aliasMatch ? aliasMatch[1] : "";
252
+ // Extract sources from frontmatter
253
+ const sourcesMatch = content.match(/sources:\s*\[([^\]]*)\]/);
254
+ const channels = sourcesMatch ? sourcesMatch[1] : "";
255
+ docs.push({ id: slug, slug, content, aliases, channels });
256
+ }
257
+ searchIndex.addAll(docs);
258
+ }
259
+ export function searchBrain(query, limit = 5) {
260
+ if (!searchIndex)
261
+ buildSearchIndex();
262
+ if (!searchIndex)
263
+ return [];
264
+ const results = searchIndex.search(query).slice(0, limit);
265
+ return results.map((r) => ({
266
+ slug: r.id,
267
+ score: r.score,
268
+ channels: r.channels || "",
269
+ }));
270
+ }
271
+ //# sourceMappingURL=brain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain.js","sourceRoot":"","sources":["../src/brain.ts"],"names":[],"mappings":"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"}