@siftd/connect-agent 0.2.27 → 0.2.29
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.
- package/README.md +57 -8
- package/dist/agent.js +17 -0
- package/dist/cli.js +6 -0
- package/dist/core/hub.d.ts +38 -1
- package/dist/core/hub.js +215 -5
- package/dist/orchestrator.js +57 -13
- package/dist/prompts/worker-system.d.ts +48 -0
- package/dist/prompts/worker-system.js +119 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,9 @@ Connect Agent runs on your machine and bridges the Connect web app to Claude Cod
|
|
|
9
9
|
- **Persistent Memory** - Remembers your preferences, projects, and context across sessions
|
|
10
10
|
- **Worker Delegation** - Delegates complex tasks to Claude Code CLI workers
|
|
11
11
|
- **Task Scheduling** - Schedule tasks for future execution
|
|
12
|
-
- **
|
|
12
|
+
- **Hub System** - Organized workspace at `~/Lia-Hub/` for state management
|
|
13
|
+
- **Gallery View** - See all files your workers create in the UI
|
|
14
|
+
- **Progress Tracking** - Real-time progress bars for running workers
|
|
13
15
|
|
|
14
16
|
## Quick Start
|
|
15
17
|
|
|
@@ -23,6 +25,12 @@ npx @siftd/connect-agent pair <CODE> --api-key <YOUR_ANTHROPIC_API_KEY>
|
|
|
23
25
|
npx @siftd/connect-agent start
|
|
24
26
|
```
|
|
25
27
|
|
|
28
|
+
On first start, the agent creates `~/Lia-Hub/` with:
|
|
29
|
+
- `AGENTS.md` - Agent identity
|
|
30
|
+
- `CLAUDE.md` - Your custom instructions
|
|
31
|
+
- `LANDMARKS.md` - Current state
|
|
32
|
+
- `notebook-a/` - Working notes and worker logs
|
|
33
|
+
|
|
26
34
|
## Modes
|
|
27
35
|
|
|
28
36
|
### Orchestrator Mode (Recommended)
|
|
@@ -31,6 +39,7 @@ When you provide an Anthropic API key, the agent runs as a **master orchestrator
|
|
|
31
39
|
- Maintains persistent memory about you and your projects
|
|
32
40
|
- Delegates file/code work to Claude Code CLI workers
|
|
33
41
|
- Schedules future tasks
|
|
42
|
+
- Reads hub files for context on every startup
|
|
34
43
|
|
|
35
44
|
### Simple Relay Mode
|
|
36
45
|
Without an API key, the agent acts as a simple relay to `claude -p --continue`.
|
|
@@ -59,6 +68,24 @@ connect-agent logout
|
|
|
59
68
|
- `/reset` or `/clear` - Clear conversation history
|
|
60
69
|
- `/mode` - Check current mode (orchestrator vs simple)
|
|
61
70
|
- `/memory` - Show memory statistics
|
|
71
|
+
- `/verbose` - Toggle verbose tool output
|
|
72
|
+
|
|
73
|
+
## Hub Structure
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
~/Lia-Hub/
|
|
77
|
+
├── AGENTS.md # Agent identity and capabilities
|
|
78
|
+
├── CLAUDE.md # Your custom instructions (always followed)
|
|
79
|
+
├── LANDMARKS.md # Current state, active projects
|
|
80
|
+
├── MISTAKES.md # Lessons learned (never deleted)
|
|
81
|
+
├── notebook-a/
|
|
82
|
+
│ ├── SCRATCHPAD.md # Working notes and plans
|
|
83
|
+
│ └── WORKER-LOG.md # All worker history (auto-logged)
|
|
84
|
+
└── shared/
|
|
85
|
+
└── outputs/ # Worker output files
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Edit `CLAUDE.md` to customize the orchestrator's behavior!
|
|
62
89
|
|
|
63
90
|
## How It Works
|
|
64
91
|
|
|
@@ -72,7 +99,7 @@ connect-agent logout
|
|
|
72
99
|
┌─────────────────┐
|
|
73
100
|
│ Master │
|
|
74
101
|
│ Orchestrator │
|
|
75
|
-
│ (memory,
|
|
102
|
+
│ (memory, hub) │
|
|
76
103
|
└────────┬────────┘
|
|
77
104
|
│ delegates
|
|
78
105
|
▼
|
|
@@ -83,17 +110,32 @@ connect-agent logout
|
|
|
83
110
|
```
|
|
84
111
|
|
|
85
112
|
The orchestrator understands it's the **master**, not a worker. It:
|
|
86
|
-
1.
|
|
87
|
-
2.
|
|
88
|
-
3.
|
|
89
|
-
4.
|
|
90
|
-
5.
|
|
91
|
-
6.
|
|
113
|
+
1. Loads hub context (AGENTS.md, CLAUDE.md, LANDMARKS.md)
|
|
114
|
+
2. Receives your message from the web
|
|
115
|
+
3. Searches memory for relevant context
|
|
116
|
+
4. Decides what needs to be done
|
|
117
|
+
5. Delegates actual work to Claude Code CLI workers
|
|
118
|
+
6. Shows progress in real-time
|
|
119
|
+
7. Synthesizes results into a clear response
|
|
120
|
+
8. Logs workers to WORKER-LOG.md
|
|
121
|
+
9. Remembers important things for next time
|
|
122
|
+
|
|
123
|
+
## Customizing Worker Behavior
|
|
124
|
+
|
|
125
|
+
All worker prompts are defined in `src/prompts/worker-system.ts`. Update this file to change how ALL workers behave:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// src/prompts/worker-system.ts
|
|
129
|
+
export const WORKER_IDENTITY = `...`;
|
|
130
|
+
export const WORKER_OUTPUT_FORMAT = `...`;
|
|
131
|
+
export function buildWorkerPrompt(task, options) { ... }
|
|
132
|
+
```
|
|
92
133
|
|
|
93
134
|
## Environment Variables
|
|
94
135
|
|
|
95
136
|
- `ANTHROPIC_API_KEY` - Alternative to `--api-key` flag
|
|
96
137
|
- `VOYAGE_API_KEY` - (Optional) For better semantic search embeddings
|
|
138
|
+
- `DATABASE_URL` - (Optional) PostgreSQL for cloud memory persistence
|
|
97
139
|
|
|
98
140
|
## Requirements
|
|
99
141
|
|
|
@@ -101,6 +143,13 @@ The orchestrator understands it's the **master**, not a worker. It:
|
|
|
101
143
|
- [Claude Code CLI](https://claude.ai/code) installed and authenticated
|
|
102
144
|
- Anthropic API key (for orchestrator mode)
|
|
103
145
|
|
|
146
|
+
## Version History
|
|
147
|
+
|
|
148
|
+
- **v0.2.28** - Lia-Hub structure, CLAUDE.md support, standardized worker prompts
|
|
149
|
+
- **v0.2.27** - Fixed workers opening browser tabs
|
|
150
|
+
- **v0.2.26** - Gallery view, worker asset tracking
|
|
151
|
+
- **v0.2.25** - Progress bars, heartbeat system
|
|
152
|
+
|
|
104
153
|
## License
|
|
105
154
|
|
|
106
155
|
MIT
|
package/dist/agent.js
CHANGED
|
@@ -4,6 +4,7 @@ import { getUserId, getAnthropicApiKey, isCloudMode, getDeploymentInfo } from '.
|
|
|
4
4
|
import { MasterOrchestrator } from './orchestrator.js';
|
|
5
5
|
import { AgentWebSocket } from './websocket.js';
|
|
6
6
|
import { startHeartbeat, stopHeartbeat, getHeartbeatState } from './heartbeat.js';
|
|
7
|
+
import { loadHubContext, readScratchpad } from './core/hub.js';
|
|
7
8
|
// Strip ANSI escape codes for clean output
|
|
8
9
|
function stripAnsi(str) {
|
|
9
10
|
return str.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, '');
|
|
@@ -217,6 +218,22 @@ export async function runAgent(pollInterval = 2000) {
|
|
|
217
218
|
console.log('[AGENT] Features: Memory, Scheduling, Worker Delegation');
|
|
218
219
|
// Initialize orchestrator (indexes filesystem into memory)
|
|
219
220
|
await orchestrator.initialize();
|
|
221
|
+
// Load hub context on startup
|
|
222
|
+
const hubContext = loadHubContext();
|
|
223
|
+
if (hubContext.agentIdentity) {
|
|
224
|
+
console.log('[AGENT] Hub: AGENTS.md loaded');
|
|
225
|
+
}
|
|
226
|
+
if (hubContext.claudeMd) {
|
|
227
|
+
console.log('[AGENT] Hub: CLAUDE.md loaded');
|
|
228
|
+
}
|
|
229
|
+
if (hubContext.landmarks) {
|
|
230
|
+
console.log('[AGENT] Hub: LANDMARKS.md loaded');
|
|
231
|
+
}
|
|
232
|
+
// Check scratchpad for pending plans
|
|
233
|
+
const scratchpad = readScratchpad();
|
|
234
|
+
if (scratchpad && scratchpad.length > 100) {
|
|
235
|
+
console.log(`[AGENT] Scratchpad: ${scratchpad.length} chars of pending notes`);
|
|
236
|
+
}
|
|
220
237
|
}
|
|
221
238
|
// Start heartbeat to maintain presence in runner registry
|
|
222
239
|
const runnerType = isCloudMode() ? 'vm' : 'local';
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import ora from 'ora';
|
|
|
4
4
|
import { getServerUrl, setServerUrl, setAgentToken, setUserId, setAnthropicApiKey, getAnthropicApiKey, isConfigured, clearConfig, getConfigPath, } from './config.js';
|
|
5
5
|
import { connectWithPairingCode, checkConnection } from './api.js';
|
|
6
6
|
import { runAgent } from './agent.js';
|
|
7
|
+
import { ensureLiaHub, getLiaHubPaths } from './core/hub.js';
|
|
7
8
|
program
|
|
8
9
|
.name('connect-agent')
|
|
9
10
|
.description('Local agent for Connect web app')
|
|
@@ -61,6 +62,11 @@ program
|
|
|
61
62
|
process.exit(1);
|
|
62
63
|
}
|
|
63
64
|
spinner.succeed('Connected to server');
|
|
65
|
+
// Initialize Lia-Hub directory structure
|
|
66
|
+
ensureLiaHub();
|
|
67
|
+
const hubPaths = getLiaHubPaths();
|
|
68
|
+
console.log(`\nHub: ${hubPaths.hub}`);
|
|
69
|
+
console.log(`Notebook: ${hubPaths.notebook}`);
|
|
64
70
|
console.log(`\nServer: ${getServerUrl()}`);
|
|
65
71
|
console.log(`Poll interval: ${options.interval}ms`);
|
|
66
72
|
console.log('\nPress Ctrl+C to stop.\n');
|
package/dist/core/hub.d.ts
CHANGED
|
@@ -4,8 +4,44 @@
|
|
|
4
4
|
* Manages structured memory files in ~/.connect-hub/
|
|
5
5
|
* Human-readable markdown files that the orchestrator reads/writes.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Ensure Lia-Hub directory structure exists
|
|
9
|
+
* Called on agent start to create the orchestrator's workspace
|
|
10
|
+
*/
|
|
11
|
+
export declare function ensureLiaHub(): void;
|
|
12
|
+
/**
|
|
13
|
+
* Get paths to Lia-Hub directories
|
|
14
|
+
*/
|
|
15
|
+
export declare function getLiaHubPaths(): {
|
|
16
|
+
hub: string;
|
|
17
|
+
notebook: string;
|
|
18
|
+
drafts: string;
|
|
19
|
+
shared: string;
|
|
20
|
+
outputs: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Read the orchestrator's scratchpad
|
|
24
|
+
*/
|
|
25
|
+
export declare function readScratchpad(): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Write to the orchestrator's scratchpad
|
|
28
|
+
*/
|
|
29
|
+
export declare function writeScratchpad(content: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Append a thought or plan to the scratchpad
|
|
32
|
+
*/
|
|
33
|
+
export declare function appendToScratchpad(note: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Log a worker to the worker log
|
|
36
|
+
*/
|
|
37
|
+
export declare function logWorker(workerId: string, task: string, status: 'started' | 'completed' | 'failed' | 'timeout', duration?: number, filesCreated?: string[]): void;
|
|
38
|
+
/**
|
|
39
|
+
* Get the shared output directory path for workers
|
|
40
|
+
*/
|
|
41
|
+
export declare function getSharedOutputPath(): string;
|
|
7
42
|
export interface HubContext {
|
|
8
43
|
agentIdentity: string;
|
|
44
|
+
claudeMd: string;
|
|
9
45
|
landmarks: string;
|
|
10
46
|
projectBio: string | null;
|
|
11
47
|
projectName: string | null;
|
|
@@ -16,7 +52,8 @@ export interface HubContext {
|
|
|
16
52
|
export declare function ensureHubExists(): void;
|
|
17
53
|
/**
|
|
18
54
|
* Load hub context for a session
|
|
19
|
-
* Reads AGENTS.md, LANDMARKS.md, and relevant project bio
|
|
55
|
+
* Reads AGENTS.md, CLAUDE.md, LANDMARKS.md, and relevant project bio
|
|
56
|
+
* Checks both ~/.connect-hub and ~/Lia-Hub locations
|
|
20
57
|
*/
|
|
21
58
|
export declare function loadHubContext(message?: string): HubContext;
|
|
22
59
|
/**
|
package/dist/core/hub.js
CHANGED
|
@@ -9,6 +9,190 @@ import { join } from 'path';
|
|
|
9
9
|
import { homedir } from 'os';
|
|
10
10
|
const HUB_DIR = join(homedir(), '.connect-hub');
|
|
11
11
|
const PROJECTS_DIR = join(HUB_DIR, 'projects');
|
|
12
|
+
// Lia-Hub: The orchestrator's working directory
|
|
13
|
+
const LIA_HUB_DIR = join(homedir(), 'Lia-Hub');
|
|
14
|
+
const NOTEBOOK_A_DIR = join(LIA_HUB_DIR, 'notebook-a');
|
|
15
|
+
const DRAFTS_DIR = join(NOTEBOOK_A_DIR, 'drafts');
|
|
16
|
+
const SHARED_DIR = join(LIA_HUB_DIR, 'shared');
|
|
17
|
+
const OUTPUTS_DIR = join(SHARED_DIR, 'outputs');
|
|
18
|
+
/**
|
|
19
|
+
* Ensure Lia-Hub directory structure exists
|
|
20
|
+
* Called on agent start to create the orchestrator's workspace
|
|
21
|
+
*/
|
|
22
|
+
export function ensureLiaHub() {
|
|
23
|
+
const dirs = [
|
|
24
|
+
LIA_HUB_DIR,
|
|
25
|
+
NOTEBOOK_A_DIR,
|
|
26
|
+
DRAFTS_DIR,
|
|
27
|
+
SHARED_DIR,
|
|
28
|
+
OUTPUTS_DIR,
|
|
29
|
+
join(LIA_HUB_DIR, 'projects')
|
|
30
|
+
];
|
|
31
|
+
for (const dir of dirs) {
|
|
32
|
+
if (!existsSync(dir)) {
|
|
33
|
+
mkdirSync(dir, { recursive: true });
|
|
34
|
+
console.log(`[HUB] Created: ${dir}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Create template files if they don't exist
|
|
38
|
+
const agentsFile = join(LIA_HUB_DIR, 'AGENTS.md');
|
|
39
|
+
if (!existsSync(agentsFile)) {
|
|
40
|
+
writeFileSync(agentsFile, `# Agent Identity
|
|
41
|
+
|
|
42
|
+
You are the Master Orchestrator - the brain that coordinates Claude Code workers.
|
|
43
|
+
You remember, plan, delegate, and synthesize. Workers do the hands-on work.
|
|
44
|
+
|
|
45
|
+
## Capabilities
|
|
46
|
+
- Memory: Store and retrieve information across sessions
|
|
47
|
+
- Scheduling: Schedule future tasks
|
|
48
|
+
- Workers: Spawn Claude Code CLI for file/code operations
|
|
49
|
+
- Web: Search and fetch from the web
|
|
50
|
+
|
|
51
|
+
## Personality
|
|
52
|
+
Warm, helpful, and efficient. You coordinate to get things done.
|
|
53
|
+
`);
|
|
54
|
+
console.log('[HUB] Created AGENTS.md template');
|
|
55
|
+
}
|
|
56
|
+
const landmarksFile = join(LIA_HUB_DIR, 'LANDMARKS.md');
|
|
57
|
+
if (!existsSync(landmarksFile)) {
|
|
58
|
+
writeFileSync(landmarksFile, `# Current State
|
|
59
|
+
|
|
60
|
+
## Active Projects
|
|
61
|
+
(none yet)
|
|
62
|
+
|
|
63
|
+
## Recent Work
|
|
64
|
+
(none yet)
|
|
65
|
+
|
|
66
|
+
## Blockers
|
|
67
|
+
(none)
|
|
68
|
+
`);
|
|
69
|
+
console.log('[HUB] Created LANDMARKS.md template');
|
|
70
|
+
}
|
|
71
|
+
const scratchpadFile = join(NOTEBOOK_A_DIR, 'SCRATCHPAD.md');
|
|
72
|
+
if (!existsSync(scratchpadFile)) {
|
|
73
|
+
writeFileSync(scratchpadFile, `# Orchestrator Scratchpad
|
|
74
|
+
|
|
75
|
+
Use this file for:
|
|
76
|
+
- Planning complex tasks
|
|
77
|
+
- Working notes
|
|
78
|
+
- Organizing thoughts before acting
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
`);
|
|
83
|
+
console.log('[HUB] Created SCRATCHPAD.md template');
|
|
84
|
+
}
|
|
85
|
+
const workerLogFile = join(NOTEBOOK_A_DIR, 'WORKER-LOG.md');
|
|
86
|
+
if (!existsSync(workerLogFile)) {
|
|
87
|
+
writeFileSync(workerLogFile, `# Worker Log
|
|
88
|
+
|
|
89
|
+
Track all workers spawned and their outcomes.
|
|
90
|
+
|
|
91
|
+
| Date | Worker ID | Task | Status | Duration | Files Created |
|
|
92
|
+
|------|-----------|------|--------|----------|---------------|
|
|
93
|
+
`);
|
|
94
|
+
console.log('[HUB] Created WORKER-LOG.md template');
|
|
95
|
+
}
|
|
96
|
+
// CLAUDE.md - User-editable instructions for the orchestrator
|
|
97
|
+
const claudeFile = join(LIA_HUB_DIR, 'CLAUDE.md');
|
|
98
|
+
if (!existsSync(claudeFile)) {
|
|
99
|
+
writeFileSync(claudeFile, `# Claude Instructions
|
|
100
|
+
|
|
101
|
+
Custom instructions for the orchestrator. Edit this file to customize behavior.
|
|
102
|
+
|
|
103
|
+
## Preferences
|
|
104
|
+
- (Add your preferences here)
|
|
105
|
+
|
|
106
|
+
## Project Defaults
|
|
107
|
+
- (Add default project settings here)
|
|
108
|
+
|
|
109
|
+
## Custom Rules
|
|
110
|
+
- (Add any rules you want the orchestrator to follow)
|
|
111
|
+
`);
|
|
112
|
+
console.log('[HUB] Created CLAUDE.md template');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get paths to Lia-Hub directories
|
|
117
|
+
*/
|
|
118
|
+
export function getLiaHubPaths() {
|
|
119
|
+
return {
|
|
120
|
+
hub: LIA_HUB_DIR,
|
|
121
|
+
notebook: NOTEBOOK_A_DIR,
|
|
122
|
+
drafts: DRAFTS_DIR,
|
|
123
|
+
shared: SHARED_DIR,
|
|
124
|
+
outputs: OUTPUTS_DIR
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Read the orchestrator's scratchpad
|
|
129
|
+
*/
|
|
130
|
+
export function readScratchpad() {
|
|
131
|
+
const path = join(NOTEBOOK_A_DIR, 'SCRATCHPAD.md');
|
|
132
|
+
if (existsSync(path)) {
|
|
133
|
+
try {
|
|
134
|
+
return readFileSync(path, 'utf-8');
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
console.error('[HUB] Failed to read SCRATCHPAD.md:', e);
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Write to the orchestrator's scratchpad
|
|
145
|
+
*/
|
|
146
|
+
export function writeScratchpad(content) {
|
|
147
|
+
ensureLiaHub();
|
|
148
|
+
const path = join(NOTEBOOK_A_DIR, 'SCRATCHPAD.md');
|
|
149
|
+
try {
|
|
150
|
+
writeFileSync(path, content);
|
|
151
|
+
console.log('[HUB] Updated SCRATCHPAD.md');
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
console.error('[HUB] Failed to write SCRATCHPAD.md:', e);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Append a thought or plan to the scratchpad
|
|
159
|
+
*/
|
|
160
|
+
export function appendToScratchpad(note) {
|
|
161
|
+
ensureLiaHub();
|
|
162
|
+
const path = join(NOTEBOOK_A_DIR, 'SCRATCHPAD.md');
|
|
163
|
+
const timestamp = new Date().toISOString().slice(0, 16).replace('T', ' ');
|
|
164
|
+
try {
|
|
165
|
+
appendFileSync(path, `\n## ${timestamp}\n${note}\n`);
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
console.error('[HUB] Failed to append to SCRATCHPAD.md:', e);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Log a worker to the worker log
|
|
173
|
+
*/
|
|
174
|
+
export function logWorker(workerId, task, status, duration, filesCreated) {
|
|
175
|
+
ensureLiaHub();
|
|
176
|
+
const path = join(NOTEBOOK_A_DIR, 'WORKER-LOG.md');
|
|
177
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
178
|
+
const durationStr = duration ? `${Math.round(duration)}s` : '-';
|
|
179
|
+
const filesStr = filesCreated?.length ? filesCreated.join(', ') : '-';
|
|
180
|
+
const taskShort = task.slice(0, 50).replace(/\|/g, '/');
|
|
181
|
+
const entry = `| ${date} | ${workerId} | ${taskShort} | ${status} | ${durationStr} | ${filesStr} |\n`;
|
|
182
|
+
try {
|
|
183
|
+
appendFileSync(path, entry);
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
console.error('[HUB] Failed to write worker log:', e);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get the shared output directory path for workers
|
|
191
|
+
*/
|
|
192
|
+
export function getSharedOutputPath() {
|
|
193
|
+
ensureLiaHub();
|
|
194
|
+
return OUTPUTS_DIR;
|
|
195
|
+
}
|
|
12
196
|
/**
|
|
13
197
|
* Ensure hub directory structure exists
|
|
14
198
|
*/
|
|
@@ -21,7 +205,7 @@ export function ensureHubExists() {
|
|
|
21
205
|
}
|
|
22
206
|
}
|
|
23
207
|
/**
|
|
24
|
-
* Read a hub file safely
|
|
208
|
+
* Read a hub file safely (from ~/.connect-hub)
|
|
25
209
|
*/
|
|
26
210
|
function readHubFile(filename) {
|
|
27
211
|
const path = join(HUB_DIR, filename);
|
|
@@ -36,6 +220,22 @@ function readHubFile(filename) {
|
|
|
36
220
|
}
|
|
37
221
|
return null;
|
|
38
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Read a Lia-Hub file safely (from ~/Lia-Hub)
|
|
225
|
+
*/
|
|
226
|
+
function readLiaHubFile(filename) {
|
|
227
|
+
const path = join(LIA_HUB_DIR, filename);
|
|
228
|
+
if (existsSync(path)) {
|
|
229
|
+
try {
|
|
230
|
+
return readFileSync(path, 'utf-8');
|
|
231
|
+
}
|
|
232
|
+
catch (e) {
|
|
233
|
+
console.error(`[HUB] Failed to read ${filename}:`, e);
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
39
239
|
/**
|
|
40
240
|
* Read a project bio
|
|
41
241
|
*/
|
|
@@ -76,12 +276,15 @@ function detectProject(message) {
|
|
|
76
276
|
}
|
|
77
277
|
/**
|
|
78
278
|
* Load hub context for a session
|
|
79
|
-
* Reads AGENTS.md, LANDMARKS.md, and relevant project bio
|
|
279
|
+
* Reads AGENTS.md, CLAUDE.md, LANDMARKS.md, and relevant project bio
|
|
280
|
+
* Checks both ~/.connect-hub and ~/Lia-Hub locations
|
|
80
281
|
*/
|
|
81
282
|
export function loadHubContext(message) {
|
|
82
283
|
ensureHubExists();
|
|
83
|
-
|
|
84
|
-
const
|
|
284
|
+
// Read from both locations, prefer Lia-Hub if available
|
|
285
|
+
const agentIdentity = readLiaHubFile('AGENTS.md') || readHubFile('AGENTS.md') || '';
|
|
286
|
+
const claudeMd = readLiaHubFile('CLAUDE.md') || readHubFile('CLAUDE.md') || '';
|
|
287
|
+
const landmarks = readLiaHubFile('LANDMARKS.md') || readHubFile('LANDMARKS.md') || '';
|
|
85
288
|
let projectBio = null;
|
|
86
289
|
let projectName = null;
|
|
87
290
|
// Try to detect project from message
|
|
@@ -93,6 +296,7 @@ export function loadHubContext(message) {
|
|
|
93
296
|
}
|
|
94
297
|
return {
|
|
95
298
|
agentIdentity,
|
|
299
|
+
claudeMd,
|
|
96
300
|
landmarks,
|
|
97
301
|
projectBio,
|
|
98
302
|
projectName
|
|
@@ -103,8 +307,14 @@ export function loadHubContext(message) {
|
|
|
103
307
|
*/
|
|
104
308
|
export function formatHubContext(ctx) {
|
|
105
309
|
const parts = [];
|
|
310
|
+
if (ctx.agentIdentity) {
|
|
311
|
+
parts.push(`## Agent Identity (AGENTS.md)\n\n${ctx.agentIdentity}`);
|
|
312
|
+
}
|
|
313
|
+
if (ctx.claudeMd) {
|
|
314
|
+
parts.push(`## User Instructions (CLAUDE.md)\n\n${ctx.claudeMd}`);
|
|
315
|
+
}
|
|
106
316
|
if (ctx.landmarks) {
|
|
107
|
-
parts.push(`## Current State (LANDMARKS)\n\n${ctx.landmarks}`);
|
|
317
|
+
parts.push(`## Current State (LANDMARKS.md)\n\n${ctx.landmarks}`);
|
|
108
318
|
}
|
|
109
319
|
if (ctx.projectBio && ctx.projectName) {
|
|
110
320
|
parts.push(`## Project Context: ${ctx.projectName}\n\n${ctx.projectBio}`);
|
package/dist/orchestrator.js
CHANGED
|
@@ -16,7 +16,8 @@ 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
|
+
import { loadHubContext, formatHubContext, logAction, logWorker } from './core/hub.js';
|
|
20
|
+
import { buildWorkerPrompt } from './prompts/worker-system.js';
|
|
20
21
|
/**
|
|
21
22
|
* Extract file paths from worker output
|
|
22
23
|
* Workers naturally mention files they create: "Created /tmp/foo.html", "Saved to /path/file"
|
|
@@ -110,6 +111,30 @@ AFTER EVERY SIGNIFICANT ACTION:
|
|
|
110
111
|
WHO YOU ARE:
|
|
111
112
|
Warm, helpful orchestrator. You coordinate, remember, and delegate. You're the persistent layer that makes AI feel personal - but you do it by managing workers, not by doing tasks yourself.
|
|
112
113
|
|
|
114
|
+
YOUR HUB: ~/Lia-Hub/
|
|
115
|
+
You have a personal hub at ~/Lia-Hub/ for organizing your work:
|
|
116
|
+
|
|
117
|
+
- **~/Lia-Hub/AGENTS.md** - Your identity and capabilities
|
|
118
|
+
- **~/Lia-Hub/CLAUDE.md** - User's custom instructions (always follow these)
|
|
119
|
+
- **~/Lia-Hub/LANDMARKS.md** - Current state, active projects, blockers
|
|
120
|
+
- **~/Lia-Hub/MISTAKES.md** - Lessons learned (append, never delete)
|
|
121
|
+
- **~/Lia-Hub/notebook-a/SCRATCHPAD.md** - Your working notes, plans, thoughts
|
|
122
|
+
- **~/Lia-Hub/notebook-a/WORKER-LOG.md** - Track all workers you spawn
|
|
123
|
+
- **~/Lia-Hub/shared/outputs/** - Where workers save their outputs
|
|
124
|
+
|
|
125
|
+
BEFORE STARTING COMPLEX WORK:
|
|
126
|
+
1. Check CLAUDE.md for user instructions
|
|
127
|
+
2. Read LANDMARKS.md for current context
|
|
128
|
+
3. Check MISTAKES.md for lessons learned
|
|
129
|
+
4. Write your plan to SCRATCHPAD.md
|
|
130
|
+
5. Search memory for relevant context
|
|
131
|
+
|
|
132
|
+
AFTER COMPLETING WORK:
|
|
133
|
+
1. Update LANDMARKS.md with new state
|
|
134
|
+
2. Log any mistakes to MISTAKES.md
|
|
135
|
+
3. Clear completed items from SCRATCHPAD.md
|
|
136
|
+
4. Remember important learnings
|
|
137
|
+
|
|
113
138
|
SLASH COMMANDS:
|
|
114
139
|
- /reset - Clear conversation and memory context
|
|
115
140
|
- /mode - Show current mode and capabilities
|
|
@@ -438,11 +463,26 @@ export class MasterOrchestrator {
|
|
|
438
463
|
const memoryContext = await this.getMemoryContext(message);
|
|
439
464
|
// Build system prompt with hub context, genesis knowledge, and memory context
|
|
440
465
|
const genesisKnowledge = getKnowledgeForPrompt();
|
|
441
|
-
let systemWithContext = SYSTEM_PROMPT
|
|
442
|
-
//
|
|
466
|
+
let systemWithContext = SYSTEM_PROMPT;
|
|
467
|
+
// CRITICAL: Inject hub context IMMEDIATELY after system prompt
|
|
468
|
+
// This ensures the orchestrator sees AGENTS.md, CLAUDE.md, LANDMARKS.md content
|
|
443
469
|
if (hubContextStr) {
|
|
444
|
-
systemWithContext +=
|
|
470
|
+
systemWithContext += `
|
|
471
|
+
|
|
472
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
473
|
+
YOUR HUB FILES (READ THESE FIRST!)
|
|
474
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
475
|
+
|
|
476
|
+
The following are the CURRENT CONTENTS of your hub files at ~/Lia-Hub/.
|
|
477
|
+
ALWAYS check these before responding - especially CLAUDE.md for user instructions.
|
|
478
|
+
|
|
479
|
+
${hubContextStr}
|
|
480
|
+
|
|
481
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
482
|
+
`;
|
|
445
483
|
}
|
|
484
|
+
// Add genesis knowledge and memory context
|
|
485
|
+
systemWithContext += genesisKnowledge;
|
|
446
486
|
if (memoryContext) {
|
|
447
487
|
systemWithContext += `\n\nRELEVANT MEMORIES:\n${memoryContext}`;
|
|
448
488
|
}
|
|
@@ -955,13 +995,13 @@ Be specific about what you want done.`,
|
|
|
955
995
|
async delegateToWorker(task, context, workingDir) {
|
|
956
996
|
const id = `worker_${Date.now()}_${++this.jobCounter}`;
|
|
957
997
|
const cwd = workingDir || this.workspaceDir;
|
|
958
|
-
//
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
}
|
|
963
|
-
//
|
|
964
|
-
|
|
998
|
+
// Use standardized worker prompt from prompts module
|
|
999
|
+
const prompt = buildWorkerPrompt(task, {
|
|
1000
|
+
jobId: id,
|
|
1001
|
+
context
|
|
1002
|
+
});
|
|
1003
|
+
// Log worker start to hub
|
|
1004
|
+
logWorker(id, task, 'started');
|
|
965
1005
|
console.log(`[ORCHESTRATOR] Worker ${id} starting: ${task.slice(0, 80)}...`);
|
|
966
1006
|
// Estimate task duration
|
|
967
1007
|
const estimatedTime = this.estimateTaskDuration(task);
|
|
@@ -1012,12 +1052,16 @@ Be specific about what you want done.`,
|
|
|
1012
1052
|
clearTimeout(timeout);
|
|
1013
1053
|
job.status = code === 0 ? 'completed' : 'failed';
|
|
1014
1054
|
job.endTime = Date.now();
|
|
1015
|
-
const duration =
|
|
1016
|
-
console.log(`[ORCHESTRATOR] Worker ${id} done in ${duration}s`);
|
|
1055
|
+
const duration = (job.endTime - job.startTime) / 1000;
|
|
1056
|
+
console.log(`[ORCHESTRATOR] Worker ${id} done in ${Math.round(duration)}s`);
|
|
1017
1057
|
const result = job.output.trim() || '(No output)';
|
|
1018
1058
|
// Extract files from worker output (workers naturally mention what they created)
|
|
1019
1059
|
const assets = extractFilesFromOutput(result);
|
|
1020
1060
|
job.assets = assets;
|
|
1061
|
+
// Log worker completion to hub
|
|
1062
|
+
const status = code === 0 ? 'completed' : 'failed';
|
|
1063
|
+
const filesCreated = assets.map(a => a.name);
|
|
1064
|
+
logWorker(id, job.task, status, duration, filesCreated.length > 0 ? filesCreated : undefined);
|
|
1021
1065
|
if (assets.length > 0) {
|
|
1022
1066
|
console.log(`[ORCHESTRATOR] Worker ${id} created ${assets.length} files: ${assets.map(a => a.name).join(', ')}`);
|
|
1023
1067
|
// Store in memory for gallery queries
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized Worker System Prompts
|
|
3
|
+
*
|
|
4
|
+
* All workers receive consistent instructions through these prompts.
|
|
5
|
+
* UPDATE THIS FILE to change how all workers behave.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Core worker identity and rules
|
|
9
|
+
* This is the foundation every worker receives
|
|
10
|
+
*/
|
|
11
|
+
export declare const WORKER_IDENTITY = "## Worker Identity\nYou are a Claude Code CLI worker executing a delegated task.\nYou report to the Master Orchestrator.\n\n## Critical Rules\n1. Do NOT open browsers or start servers\n2. Do NOT ask questions - just do the work\n3. Output files to the shared workspace unless specified\n4. Report file paths you create at the end\n5. Be concise and efficient";
|
|
12
|
+
/**
|
|
13
|
+
* Output format workers should follow
|
|
14
|
+
*/
|
|
15
|
+
export declare const WORKER_OUTPUT_FORMAT = "## Output Format\nWhen done, clearly list:\n- Files created: /path/to/file.ext\n- Files modified: /path/to/file.ext\n- Key findings: brief summary\n\nBe concise. Execute efficiently.";
|
|
16
|
+
/**
|
|
17
|
+
* Instructions for workers to contribute back to memory
|
|
18
|
+
* Workers can add to orchestrator's memory using these patterns
|
|
19
|
+
*/
|
|
20
|
+
export declare const WORKER_MEMORY_INSTRUCTIONS = "## Memory Contributions (Optional)\nIf you discover important information the orchestrator should remember:\n- [MEMORY] type=semantic | content=Important fact or preference\n- [MEMORY] type=procedural | content=How to do something\n- [MEMORY] type=episodic | content=What happened and when\n\nExample: [MEMORY] type=semantic | content=User prefers tabs over spaces in TypeScript files";
|
|
21
|
+
/**
|
|
22
|
+
* Instructions for worker-to-worker coordination
|
|
23
|
+
*/
|
|
24
|
+
export declare const WORKER_COORDINATION = "## Coordination (For Parallel Workers)\nShare data with other workers:\n- [SHARE] key=myKey | value={\"data\": \"here\"}\n- [SIGNAL] name=taskComplete | data={\"result\": \"success\"}\n- [MESSAGE] to=otherWorkerId | content=Your message";
|
|
25
|
+
/**
|
|
26
|
+
* Logging instructions to prevent data loss
|
|
27
|
+
*/
|
|
28
|
+
export declare function getWorkerLoggingInstructions(jobId: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Get the full worker system prompt
|
|
31
|
+
* This is what every worker receives at the start
|
|
32
|
+
*/
|
|
33
|
+
export declare function getWorkerSystemPrompt(): string;
|
|
34
|
+
/**
|
|
35
|
+
* Build a complete worker prompt with task and context
|
|
36
|
+
* Use this to construct prompts for spawn_worker and delegate_to_worker
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildWorkerPrompt(task: string, options?: {
|
|
39
|
+
jobId?: string;
|
|
40
|
+
context?: string;
|
|
41
|
+
includeCoordination?: boolean;
|
|
42
|
+
customInstructions?: string;
|
|
43
|
+
}): string;
|
|
44
|
+
/**
|
|
45
|
+
* Short worker prompt for simple tasks
|
|
46
|
+
* Lighter version for quick operations
|
|
47
|
+
*/
|
|
48
|
+
export declare function getQuickWorkerPrompt(task: string, context?: string): string;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized Worker System Prompts
|
|
3
|
+
*
|
|
4
|
+
* All workers receive consistent instructions through these prompts.
|
|
5
|
+
* UPDATE THIS FILE to change how all workers behave.
|
|
6
|
+
*/
|
|
7
|
+
import { getSharedOutputPath } from '../core/hub.js';
|
|
8
|
+
/**
|
|
9
|
+
* Core worker identity and rules
|
|
10
|
+
* This is the foundation every worker receives
|
|
11
|
+
*/
|
|
12
|
+
export const WORKER_IDENTITY = `## Worker Identity
|
|
13
|
+
You are a Claude Code CLI worker executing a delegated task.
|
|
14
|
+
You report to the Master Orchestrator.
|
|
15
|
+
|
|
16
|
+
## Critical Rules
|
|
17
|
+
1. Do NOT open browsers or start servers
|
|
18
|
+
2. Do NOT ask questions - just do the work
|
|
19
|
+
3. Output files to the shared workspace unless specified
|
|
20
|
+
4. Report file paths you create at the end
|
|
21
|
+
5. Be concise and efficient`;
|
|
22
|
+
/**
|
|
23
|
+
* Output format workers should follow
|
|
24
|
+
*/
|
|
25
|
+
export const WORKER_OUTPUT_FORMAT = `## Output Format
|
|
26
|
+
When done, clearly list:
|
|
27
|
+
- Files created: /path/to/file.ext
|
|
28
|
+
- Files modified: /path/to/file.ext
|
|
29
|
+
- Key findings: brief summary
|
|
30
|
+
|
|
31
|
+
Be concise. Execute efficiently.`;
|
|
32
|
+
/**
|
|
33
|
+
* Instructions for workers to contribute back to memory
|
|
34
|
+
* Workers can add to orchestrator's memory using these patterns
|
|
35
|
+
*/
|
|
36
|
+
export const WORKER_MEMORY_INSTRUCTIONS = `## Memory Contributions (Optional)
|
|
37
|
+
If you discover important information the orchestrator should remember:
|
|
38
|
+
- [MEMORY] type=semantic | content=Important fact or preference
|
|
39
|
+
- [MEMORY] type=procedural | content=How to do something
|
|
40
|
+
- [MEMORY] type=episodic | content=What happened and when
|
|
41
|
+
|
|
42
|
+
Example: [MEMORY] type=semantic | content=User prefers tabs over spaces in TypeScript files`;
|
|
43
|
+
/**
|
|
44
|
+
* Instructions for worker-to-worker coordination
|
|
45
|
+
*/
|
|
46
|
+
export const WORKER_COORDINATION = `## Coordination (For Parallel Workers)
|
|
47
|
+
Share data with other workers:
|
|
48
|
+
- [SHARE] key=myKey | value={"data": "here"}
|
|
49
|
+
- [SIGNAL] name=taskComplete | data={"result": "success"}
|
|
50
|
+
- [MESSAGE] to=otherWorkerId | content=Your message`;
|
|
51
|
+
/**
|
|
52
|
+
* Logging instructions to prevent data loss
|
|
53
|
+
*/
|
|
54
|
+
export function getWorkerLoggingInstructions(jobId) {
|
|
55
|
+
const logFile = `/tmp/worker-${jobId}-log.txt`;
|
|
56
|
+
return `## Progress & Logging
|
|
57
|
+
- Output findings as you go, don't wait until the end
|
|
58
|
+
- Print discoveries and insights immediately as you find them
|
|
59
|
+
- Report on each file/step before moving to the next
|
|
60
|
+
|
|
61
|
+
REQUIRED - Log Export:
|
|
62
|
+
At the END of your work, create a final log file at: ${logFile}
|
|
63
|
+
Include: job_id=${jobId}, timestamp, summary of work done, files modified, key findings.
|
|
64
|
+
This ensures nothing is lost even if your output gets truncated.`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get the full worker system prompt
|
|
68
|
+
* This is what every worker receives at the start
|
|
69
|
+
*/
|
|
70
|
+
export function getWorkerSystemPrompt() {
|
|
71
|
+
const sharedPath = getSharedOutputPath();
|
|
72
|
+
return `${WORKER_IDENTITY}
|
|
73
|
+
|
|
74
|
+
## Shared Workspace
|
|
75
|
+
Save output files to: ${sharedPath}
|
|
76
|
+
The orchestrator and other workers can access files here.
|
|
77
|
+
|
|
78
|
+
${WORKER_OUTPUT_FORMAT}
|
|
79
|
+
|
|
80
|
+
${WORKER_MEMORY_INSTRUCTIONS}`;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Build a complete worker prompt with task and context
|
|
84
|
+
* Use this to construct prompts for spawn_worker and delegate_to_worker
|
|
85
|
+
*/
|
|
86
|
+
export function buildWorkerPrompt(task, options = {}) {
|
|
87
|
+
const parts = [getWorkerSystemPrompt()];
|
|
88
|
+
// Add coordination instructions for parallel workers
|
|
89
|
+
if (options.includeCoordination) {
|
|
90
|
+
parts.push(WORKER_COORDINATION);
|
|
91
|
+
}
|
|
92
|
+
// Add logging instructions with job ID
|
|
93
|
+
if (options.jobId) {
|
|
94
|
+
parts.push(getWorkerLoggingInstructions(options.jobId));
|
|
95
|
+
}
|
|
96
|
+
// Add custom instructions if provided
|
|
97
|
+
if (options.customInstructions) {
|
|
98
|
+
parts.push(`## Additional Instructions\n${options.customInstructions}`);
|
|
99
|
+
}
|
|
100
|
+
// Add context if provided
|
|
101
|
+
if (options.context) {
|
|
102
|
+
parts.push(`## Context\n${options.context}`);
|
|
103
|
+
}
|
|
104
|
+
// Add the actual task
|
|
105
|
+
parts.push(`## Task\n${task}`);
|
|
106
|
+
return parts.join('\n\n');
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Short worker prompt for simple tasks
|
|
110
|
+
* Lighter version for quick operations
|
|
111
|
+
*/
|
|
112
|
+
export function getQuickWorkerPrompt(task, context) {
|
|
113
|
+
let prompt = task;
|
|
114
|
+
if (context) {
|
|
115
|
+
prompt = `Context: ${context}\n\nTask: ${task}`;
|
|
116
|
+
}
|
|
117
|
+
prompt += `\n\nBe concise. Output results directly. Do NOT open browsers or start servers - just create files and report paths.`;
|
|
118
|
+
return prompt;
|
|
119
|
+
}
|