@siftd/connect-agent 0.2.20 → 0.2.21

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.
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Hub - The Agent's Brain Home
3
+ *
4
+ * Manages structured memory files in ~/.connect-hub/
5
+ * Human-readable markdown files that the orchestrator reads/writes.
6
+ */
7
+ export interface HubContext {
8
+ agentIdentity: string;
9
+ landmarks: string;
10
+ projectBio: string | null;
11
+ projectName: string | null;
12
+ }
13
+ /**
14
+ * Ensure hub directory structure exists
15
+ */
16
+ export declare function ensureHubExists(): void;
17
+ /**
18
+ * Load hub context for a session
19
+ * Reads AGENTS.md, LANDMARKS.md, and relevant project bio
20
+ */
21
+ export declare function loadHubContext(message?: string): HubContext;
22
+ /**
23
+ * Format hub context for inclusion in system prompt
24
+ */
25
+ export declare function formatHubContext(ctx: HubContext): string;
26
+ /**
27
+ * Append to action log
28
+ */
29
+ export declare function logAction(action: string, project?: string, details?: string): void;
30
+ /**
31
+ * Update or create a project bio
32
+ */
33
+ export declare function updateProjectBio(projectName: string, content: string): void;
34
+ /**
35
+ * Append learning to a project bio
36
+ */
37
+ export declare function appendToProjectBio(projectName: string, section: string, content: string): void;
38
+ /**
39
+ * Log a mistake for learning
40
+ */
41
+ export declare function logMistake(what: string, rootCause: string, fix: string, lesson: string): void;
42
+ /**
43
+ * Update LANDMARKS.md with current state
44
+ */
45
+ export declare function updateLandmarks(content: string): void;
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Hub - The Agent's Brain Home
3
+ *
4
+ * Manages structured memory files in ~/.connect-hub/
5
+ * Human-readable markdown files that the orchestrator reads/writes.
6
+ */
7
+ import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from 'fs';
8
+ import { join } from 'path';
9
+ import { homedir } from 'os';
10
+ const HUB_DIR = join(homedir(), '.connect-hub');
11
+ const PROJECTS_DIR = join(HUB_DIR, 'projects');
12
+ /**
13
+ * Ensure hub directory structure exists
14
+ */
15
+ export function ensureHubExists() {
16
+ if (!existsSync(HUB_DIR)) {
17
+ mkdirSync(HUB_DIR, { recursive: true });
18
+ }
19
+ if (!existsSync(PROJECTS_DIR)) {
20
+ mkdirSync(PROJECTS_DIR, { recursive: true });
21
+ }
22
+ }
23
+ /**
24
+ * Read a hub file safely
25
+ */
26
+ function readHubFile(filename) {
27
+ const path = join(HUB_DIR, filename);
28
+ if (existsSync(path)) {
29
+ try {
30
+ return readFileSync(path, 'utf-8');
31
+ }
32
+ catch (e) {
33
+ console.error(`[HUB] Failed to read ${filename}:`, e);
34
+ return null;
35
+ }
36
+ }
37
+ return null;
38
+ }
39
+ /**
40
+ * Read a project bio
41
+ */
42
+ function readProjectBio(projectName) {
43
+ // Normalize project name (lowercase, hyphens)
44
+ const normalized = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
45
+ const path = join(PROJECTS_DIR, `${normalized}.md`);
46
+ if (existsSync(path)) {
47
+ try {
48
+ return readFileSync(path, 'utf-8');
49
+ }
50
+ catch (e) {
51
+ console.error(`[HUB] Failed to read project bio ${normalized}:`, e);
52
+ return null;
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+ /**
58
+ * Detect project name from message content
59
+ */
60
+ function detectProject(message) {
61
+ // Common project patterns
62
+ const patterns = [
63
+ /\b(connect[-_]?app[-_]?2?)\b/i,
64
+ /\b(lia[-_]?live)\b/i,
65
+ /\b(game[-_]?001)\b/i,
66
+ /\bproj(?:ect)?[-_:]?\s*([a-z0-9-]+)/i,
67
+ /\bin\s+([a-z0-9-]+)\s+(?:project|repo|codebase)/i,
68
+ ];
69
+ for (const pattern of patterns) {
70
+ const match = message.match(pattern);
71
+ if (match) {
72
+ return match[1].toLowerCase().replace(/[^a-z0-9-]/g, '-');
73
+ }
74
+ }
75
+ return null;
76
+ }
77
+ /**
78
+ * Load hub context for a session
79
+ * Reads AGENTS.md, LANDMARKS.md, and relevant project bio
80
+ */
81
+ export function loadHubContext(message) {
82
+ ensureHubExists();
83
+ const agentIdentity = readHubFile('AGENTS.md') || '';
84
+ const landmarks = readHubFile('LANDMARKS.md') || '';
85
+ let projectBio = null;
86
+ let projectName = null;
87
+ // Try to detect project from message
88
+ if (message) {
89
+ projectName = detectProject(message);
90
+ if (projectName) {
91
+ projectBio = readProjectBio(projectName);
92
+ }
93
+ }
94
+ return {
95
+ agentIdentity,
96
+ landmarks,
97
+ projectBio,
98
+ projectName
99
+ };
100
+ }
101
+ /**
102
+ * Format hub context for inclusion in system prompt
103
+ */
104
+ export function formatHubContext(ctx) {
105
+ const parts = [];
106
+ if (ctx.landmarks) {
107
+ parts.push(`## Current State (LANDMARKS)\n\n${ctx.landmarks}`);
108
+ }
109
+ if (ctx.projectBio && ctx.projectName) {
110
+ parts.push(`## Project Context: ${ctx.projectName}\n\n${ctx.projectBio}`);
111
+ }
112
+ return parts.join('\n\n---\n\n');
113
+ }
114
+ /**
115
+ * Append to action log
116
+ */
117
+ export function logAction(action, project, details) {
118
+ ensureHubExists();
119
+ const logPath = join(HUB_DIR, 'ACTION-LOG.md');
120
+ const now = new Date();
121
+ const timestamp = now.toISOString().slice(0, 16).replace('T', ' ');
122
+ const projectPart = project ? ` — ${project}` : '';
123
+ const detailsPart = details ? ` — ${details}` : '';
124
+ const entry = `${timestamp}${projectPart} — ${action}${detailsPart}\n`;
125
+ try {
126
+ appendFileSync(logPath, entry);
127
+ }
128
+ catch (e) {
129
+ console.error('[HUB] Failed to write action log:', e);
130
+ }
131
+ }
132
+ /**
133
+ * Update or create a project bio
134
+ */
135
+ export function updateProjectBio(projectName, content) {
136
+ ensureHubExists();
137
+ const normalized = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
138
+ const path = join(PROJECTS_DIR, `${normalized}.md`);
139
+ try {
140
+ writeFileSync(path, content);
141
+ console.log(`[HUB] Updated project bio: ${normalized}`);
142
+ }
143
+ catch (e) {
144
+ console.error(`[HUB] Failed to write project bio ${normalized}:`, e);
145
+ }
146
+ }
147
+ /**
148
+ * Append learning to a project bio
149
+ */
150
+ export function appendToProjectBio(projectName, section, content) {
151
+ ensureHubExists();
152
+ const normalized = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
153
+ const path = join(PROJECTS_DIR, `${normalized}.md`);
154
+ try {
155
+ let existing = '';
156
+ if (existsSync(path)) {
157
+ existing = readFileSync(path, 'utf-8');
158
+ }
159
+ else {
160
+ existing = `# Project: ${projectName}\n\n`;
161
+ }
162
+ // Append under the section
163
+ const sectionHeader = `## ${section}`;
164
+ if (existing.includes(sectionHeader)) {
165
+ // Append to existing section
166
+ const parts = existing.split(sectionHeader);
167
+ const afterSection = parts[1] || '';
168
+ const nextSectionIndex = afterSection.indexOf('\n## ');
169
+ if (nextSectionIndex > -1) {
170
+ // Insert before next section
171
+ const sectionContent = afterSection.slice(0, nextSectionIndex);
172
+ const rest = afterSection.slice(nextSectionIndex);
173
+ existing = parts[0] + sectionHeader + sectionContent + '\n' + content + '\n' + rest;
174
+ }
175
+ else {
176
+ // Append at end
177
+ existing = existing + '\n' + content + '\n';
178
+ }
179
+ }
180
+ else {
181
+ // Create new section
182
+ existing = existing + `\n${sectionHeader}\n\n${content}\n`;
183
+ }
184
+ writeFileSync(path, existing);
185
+ }
186
+ catch (e) {
187
+ console.error(`[HUB] Failed to append to project bio ${normalized}:`, e);
188
+ }
189
+ }
190
+ /**
191
+ * Log a mistake for learning
192
+ */
193
+ export function logMistake(what, rootCause, fix, lesson) {
194
+ ensureHubExists();
195
+ const path = join(HUB_DIR, 'MISTAKES.md');
196
+ const now = new Date();
197
+ const date = now.toISOString().slice(0, 10);
198
+ const entry = `
199
+ ---
200
+
201
+ ## ${date} — ${what}
202
+
203
+ **What happened**: ${what}
204
+
205
+ **Root cause**: ${rootCause}
206
+
207
+ **Fix**: ${fix}
208
+
209
+ **Lesson**: ${lesson}
210
+ `;
211
+ try {
212
+ appendFileSync(path, entry);
213
+ }
214
+ catch (e) {
215
+ console.error('[HUB] Failed to write mistake log:', e);
216
+ }
217
+ }
218
+ /**
219
+ * Update LANDMARKS.md with current state
220
+ */
221
+ export function updateLandmarks(content) {
222
+ ensureHubExists();
223
+ const path = join(HUB_DIR, 'LANDMARKS.md');
224
+ try {
225
+ writeFileSync(path, content);
226
+ console.log('[HUB] Updated LANDMARKS.md');
227
+ }
228
+ catch (e) {
229
+ console.error('[HUB] Failed to update LANDMARKS.md:', e);
230
+ }
231
+ }
@@ -16,6 +16,7 @@ import { WebTools } from './tools/web.js';
16
16
  import { WorkerTools } from './tools/worker.js';
17
17
  import { SharedState } from './workers/shared-state.js';
18
18
  import { getKnowledgeForPrompt } from './genesis/index.js';
19
+ import { loadHubContext, formatHubContext, logAction } from './core/hub.js';
19
20
  const SYSTEM_PROMPT = `You are a MASTER ORCHESTRATOR - NOT a worker. You delegate ALL file/code work to Claude Code CLI workers.
20
21
 
21
22
  CRITICAL IDENTITY:
@@ -331,11 +332,18 @@ export class MasterOrchestrator {
331
332
  if (slashResponse) {
332
333
  return slashResponse;
333
334
  }
335
+ // Load hub context (AGENTS.md identity, LANDMARKS.md state, project bio if relevant)
336
+ const hubContext = loadHubContext(message);
337
+ const hubContextStr = formatHubContext(hubContext);
334
338
  // Build context from memory
335
339
  const memoryContext = await this.getMemoryContext(message);
336
- // Build system prompt with genesis knowledge and memory context
340
+ // Build system prompt with hub context, genesis knowledge, and memory context
337
341
  const genesisKnowledge = getKnowledgeForPrompt();
338
342
  let systemWithContext = SYSTEM_PROMPT + genesisKnowledge;
343
+ // Add hub context (landmarks + project bio)
344
+ if (hubContextStr) {
345
+ systemWithContext += `\n\n---\n\nHUB CONTEXT:\n${hubContextStr}`;
346
+ }
339
347
  if (memoryContext) {
340
348
  systemWithContext += `\n\nRELEVANT MEMORIES:\n${memoryContext}`;
341
349
  }
@@ -348,6 +356,11 @@ export class MasterOrchestrator {
348
356
  const response = await this.runAgentLoop(messages, systemWithContext, sendMessage, apiKey);
349
357
  // Auto-remember important things from the conversation
350
358
  await this.autoRemember(message, response);
359
+ // Log significant actions to hub
360
+ if (response.length > 100) {
361
+ const action = message.length > 50 ? message.slice(0, 50) + '...' : message;
362
+ logAction(action, hubContext.projectName || undefined, `Response: ${response.length} chars`);
363
+ }
351
364
  return response;
352
365
  }
353
366
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siftd/connect-agent",
3
- "version": "0.2.20",
3
+ "version": "0.2.21",
4
4
  "description": "Master orchestrator agent - control Claude Code remotely via web",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",