@aion0/forge 0.4.15 → 0.5.0
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/CLAUDE.md +1 -1
- package/README.md +2 -2
- package/RELEASE_NOTES.md +170 -13
- package/app/api/agents/route.ts +17 -0
- package/app/api/delivery/[id]/route.ts +62 -0
- package/app/api/delivery/route.ts +40 -0
- package/app/api/mobile-chat/route.ts +13 -7
- package/app/api/monitor/route.ts +10 -6
- package/app/api/pipelines/[id]/route.ts +16 -3
- package/app/api/tasks/route.ts +2 -1
- package/app/api/workspace/[id]/agents/route.ts +35 -0
- package/app/api/workspace/[id]/memory/route.ts +23 -0
- package/app/api/workspace/[id]/smith/route.ts +22 -0
- package/app/api/workspace/[id]/stream/route.ts +28 -0
- package/app/api/workspace/route.ts +100 -0
- package/app/global-error.tsx +10 -4
- package/app/icon.ico +0 -0
- package/app/layout.tsx +2 -2
- package/app/login/LoginForm.tsx +96 -0
- package/app/login/page.tsx +7 -98
- package/app/page.tsx +2 -2
- package/bin/forge-server.mjs +23 -4
- package/check-forge-status.sh +9 -0
- package/cli/mw.ts +2 -2
- package/components/ConversationEditor.tsx +411 -0
- package/components/ConversationGraphView.tsx +347 -0
- package/components/ConversationTerminalView.tsx +303 -0
- package/components/Dashboard.tsx +36 -39
- package/components/DashboardWrapper.tsx +9 -0
- package/components/DeliveryFlowEditor.tsx +491 -0
- package/components/DeliveryList.tsx +230 -0
- package/components/DeliveryWorkspace.tsx +589 -0
- package/components/DocTerminal.tsx +12 -4
- package/components/DocsViewer.tsx +10 -2
- package/components/HelpTerminal.tsx +13 -8
- package/components/InlinePipelineView.tsx +111 -0
- package/components/MobileView.tsx +20 -0
- package/components/MonitorPanel.tsx +9 -4
- package/components/NewTaskModal.tsx +32 -0
- package/components/PipelineEditor.tsx +49 -6
- package/components/PipelineView.tsx +482 -64
- package/components/ProjectDetail.tsx +314 -56
- package/components/ProjectManager.tsx +49 -4
- package/components/SessionView.tsx +27 -13
- package/components/SettingsModal.tsx +790 -124
- package/components/SkillsPanel.tsx +34 -8
- package/components/TaskBoard.tsx +3 -0
- package/components/WebTerminal.tsx +259 -45
- package/components/WorkspaceTree.tsx +221 -0
- package/components/WorkspaceView.tsx +2224 -0
- package/docs/LOCAL-DEPLOY.md +15 -15
- package/install.sh +2 -2
- package/lib/agents/claude-adapter.ts +104 -0
- package/lib/agents/generic-adapter.ts +64 -0
- package/lib/agents/index.ts +242 -0
- package/lib/agents/types.ts +70 -0
- package/lib/artifacts.ts +106 -0
- package/lib/cloudflared.ts +1 -1
- package/lib/delivery.ts +787 -0
- package/lib/forge-skills/forge-inbox.md +37 -0
- package/lib/forge-skills/forge-send.md +40 -0
- package/lib/forge-skills/forge-status.md +32 -0
- package/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/lib/help-docs/00-overview.md +8 -2
- package/lib/help-docs/01-settings.md +159 -2
- package/lib/help-docs/05-pipelines.md +95 -6
- package/lib/help-docs/07-projects.md +35 -1
- package/lib/help-docs/11-workspace.md +204 -0
- package/lib/help-docs/CLAUDE.md +5 -2
- package/lib/init.ts +62 -12
- package/lib/pipeline.ts +537 -1
- package/lib/settings.ts +115 -22
- package/lib/skills.ts +249 -372
- package/lib/task-manager.ts +113 -33
- package/lib/telegram-bot.ts +33 -1
- package/lib/telegram-standalone.ts +1 -1
- package/lib/terminal-server.ts +2 -2
- package/lib/terminal-standalone.ts +1 -1
- package/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/lib/workspace/agent-bus.ts +416 -0
- package/lib/workspace/agent-worker.ts +667 -0
- package/lib/workspace/backends/api-backend.ts +262 -0
- package/lib/workspace/backends/cli-backend.ts +479 -0
- package/lib/workspace/index.ts +82 -0
- package/lib/workspace/manager.ts +136 -0
- package/lib/workspace/orchestrator.ts +1804 -0
- package/lib/workspace/persistence.ts +310 -0
- package/lib/workspace/presets.ts +170 -0
- package/lib/workspace/skill-installer.ts +188 -0
- package/lib/workspace/smith-memory.ts +498 -0
- package/lib/workspace/types.ts +231 -0
- package/lib/workspace/watch-manager.ts +288 -0
- package/lib/workspace-standalone.ts +790 -0
- package/middleware.ts +1 -0
- package/next-env.d.ts +1 -1
- package/package.json +5 -2
- package/src/config/index.ts +13 -2
- package/src/core/db/database.ts +1 -0
- package/start.sh +10 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
// ─── Types ───────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
interface Project {
|
|
8
|
+
name: string;
|
|
9
|
+
path: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface AgentInfo {
|
|
13
|
+
id: string;
|
|
14
|
+
label: string;
|
|
15
|
+
icon: string;
|
|
16
|
+
type?: 'agent' | 'input';
|
|
17
|
+
status: string;
|
|
18
|
+
currentStep?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface WorkspaceSummary {
|
|
22
|
+
id: string;
|
|
23
|
+
projectName: string;
|
|
24
|
+
projectPath: string;
|
|
25
|
+
agents: AgentInfo[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ─── Status indicators ───────────────────────────────────
|
|
29
|
+
|
|
30
|
+
const STATUS_DOT: Record<string, { char: string; color: string }> = {
|
|
31
|
+
idle: { char: '○', color: 'text-gray-500' },
|
|
32
|
+
running: { char: '◐', color: 'text-green-400' },
|
|
33
|
+
paused: { char: '⏸', color: 'text-yellow-400' },
|
|
34
|
+
waiting_approval: { char: '⏳', color: 'text-yellow-400' },
|
|
35
|
+
done: { char: '●', color: 'text-blue-400' },
|
|
36
|
+
failed: { char: '✕', color: 'text-red-400' },
|
|
37
|
+
interrupted: { char: '◌', color: 'text-gray-400' },
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// ─── Component ───────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
export default function WorkspaceTree({
|
|
43
|
+
activeProjectPath,
|
|
44
|
+
onSelectProject,
|
|
45
|
+
onSelectAgent,
|
|
46
|
+
onCreateWorkspace,
|
|
47
|
+
}: {
|
|
48
|
+
activeProjectPath: string | null;
|
|
49
|
+
onSelectProject: (project: Project) => void;
|
|
50
|
+
onSelectAgent: (agentId: string) => void;
|
|
51
|
+
onCreateWorkspace: () => void;
|
|
52
|
+
}) {
|
|
53
|
+
const [projects, setProjects] = useState<Project[]>([]);
|
|
54
|
+
const [workspaces, setWorkspaces] = useState<Map<string, WorkspaceSummary>>(new Map());
|
|
55
|
+
const [expanded, setExpanded] = useState<Set<string>>(new Set());
|
|
56
|
+
|
|
57
|
+
// Fetch projects
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
fetch('/api/projects').then(r => r.json())
|
|
60
|
+
.then((data: any[]) => {
|
|
61
|
+
const projs = data.map(p => ({ name: p.name, path: p.path }));
|
|
62
|
+
setProjects(projs);
|
|
63
|
+
// Auto-expand active project
|
|
64
|
+
if (activeProjectPath) {
|
|
65
|
+
setExpanded(prev => new Set([...prev, activeProjectPath]));
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
.catch(() => {});
|
|
69
|
+
}, [activeProjectPath]);
|
|
70
|
+
|
|
71
|
+
// Fetch workspace summaries for expanded projects
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
for (const path of expanded) {
|
|
74
|
+
if (workspaces.has(path)) continue;
|
|
75
|
+
fetch(`/api/workspace?projectPath=${encodeURIComponent(path)}`)
|
|
76
|
+
.then(r => r.json())
|
|
77
|
+
.then(ws => {
|
|
78
|
+
if (!ws?.id) return;
|
|
79
|
+
// Fetch agent states
|
|
80
|
+
fetch(`/api/workspace/${ws.id}/agents`).then(r => r.json())
|
|
81
|
+
.then(data => {
|
|
82
|
+
const agents: AgentInfo[] = (data.agents || []).map((a: any) => {
|
|
83
|
+
const state = data.states?.[a.id] || {};
|
|
84
|
+
return {
|
|
85
|
+
id: a.id,
|
|
86
|
+
label: a.label,
|
|
87
|
+
icon: a.icon,
|
|
88
|
+
type: a.type,
|
|
89
|
+
status: state.status || 'idle',
|
|
90
|
+
currentStep: state.currentStep !== undefined
|
|
91
|
+
? a.steps?.[state.currentStep]?.label
|
|
92
|
+
: undefined,
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
setWorkspaces(prev => new Map([...prev, [path, {
|
|
96
|
+
id: ws.id,
|
|
97
|
+
projectName: ws.projectName,
|
|
98
|
+
projectPath: ws.projectPath,
|
|
99
|
+
agents,
|
|
100
|
+
}]]));
|
|
101
|
+
})
|
|
102
|
+
.catch(() => {});
|
|
103
|
+
})
|
|
104
|
+
.catch(() => {
|
|
105
|
+
// No workspace for this project
|
|
106
|
+
setWorkspaces(prev => new Map([...prev, [path, {
|
|
107
|
+
id: '',
|
|
108
|
+
projectName: '',
|
|
109
|
+
projectPath: path,
|
|
110
|
+
agents: [],
|
|
111
|
+
}]]));
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}, [expanded]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
115
|
+
|
|
116
|
+
const toggleExpand = (path: string) => {
|
|
117
|
+
setExpanded(prev => {
|
|
118
|
+
const next = new Set(prev);
|
|
119
|
+
if (next.has(path)) next.delete(path); else next.add(path);
|
|
120
|
+
return next;
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div className="flex flex-col h-full overflow-hidden">
|
|
126
|
+
{/* Header */}
|
|
127
|
+
<div className="flex items-center gap-2 px-3 py-2 border-b border-[var(--border)] shrink-0">
|
|
128
|
+
<span className="text-xs font-bold text-[var(--text-primary)]">Workspace</span>
|
|
129
|
+
<button onClick={onCreateWorkspace}
|
|
130
|
+
className="text-[8px] px-1.5 py-0.5 rounded border border-[var(--border)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] ml-auto">
|
|
131
|
+
+
|
|
132
|
+
</button>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
{/* Tree */}
|
|
136
|
+
<div className="flex-1 overflow-auto py-1">
|
|
137
|
+
{projects.length === 0 && (
|
|
138
|
+
<div className="text-[10px] text-[var(--text-secondary)] text-center mt-4">No projects</div>
|
|
139
|
+
)}
|
|
140
|
+
{projects.map(project => {
|
|
141
|
+
const isExpanded = expanded.has(project.path);
|
|
142
|
+
const isActive = project.path === activeProjectPath;
|
|
143
|
+
const ws = workspaces.get(project.path);
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<div key={project.path}>
|
|
147
|
+
{/* Project row */}
|
|
148
|
+
<div
|
|
149
|
+
className={`flex items-center gap-1.5 px-2 py-1 cursor-pointer text-[11px] hover:bg-[var(--bg-secondary)] ${
|
|
150
|
+
isActive ? 'bg-[var(--bg-secondary)] text-[var(--text-primary)]' : 'text-[var(--text-secondary)]'
|
|
151
|
+
}`}
|
|
152
|
+
onClick={() => {
|
|
153
|
+
toggleExpand(project.path);
|
|
154
|
+
onSelectProject(project);
|
|
155
|
+
}}
|
|
156
|
+
>
|
|
157
|
+
<span className="text-[8px] w-3 text-center shrink-0">{isExpanded ? '▾' : '▸'}</span>
|
|
158
|
+
<span className="text-[10px]">📂</span>
|
|
159
|
+
<span className="truncate flex-1">{project.name}</span>
|
|
160
|
+
{ws && ws.agents.length > 0 && (
|
|
161
|
+
<span className="text-[8px] text-[var(--text-secondary)]">{ws.agents.length}</span>
|
|
162
|
+
)}
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
{/* Agents */}
|
|
166
|
+
{isExpanded && ws && ws.agents.length > 0 && (
|
|
167
|
+
<div className="ml-4">
|
|
168
|
+
{ws.agents.map(agent => {
|
|
169
|
+
const dot = STATUS_DOT[agent.status] || STATUS_DOT.idle;
|
|
170
|
+
return (
|
|
171
|
+
<div
|
|
172
|
+
key={agent.id}
|
|
173
|
+
className="flex items-center gap-1.5 px-2 py-0.5 cursor-pointer text-[10px] hover:bg-[var(--bg-secondary)] text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
|
|
174
|
+
onClick={(e) => {
|
|
175
|
+
e.stopPropagation();
|
|
176
|
+
onSelectProject(project);
|
|
177
|
+
setTimeout(() => onSelectAgent(agent.id), 100);
|
|
178
|
+
}}
|
|
179
|
+
>
|
|
180
|
+
<span className="text-[9px]">{agent.icon}</span>
|
|
181
|
+
<span className="truncate flex-1">{agent.label}</span>
|
|
182
|
+
<span className={`text-[8px] ${dot.color}`} title={agent.status}>
|
|
183
|
+
{dot.char}
|
|
184
|
+
</span>
|
|
185
|
+
{agent.status === 'running' && agent.currentStep && (
|
|
186
|
+
<span className="text-[7px] text-green-400/60 truncate max-w-16">{agent.currentStep}</span>
|
|
187
|
+
)}
|
|
188
|
+
</div>
|
|
189
|
+
);
|
|
190
|
+
})}
|
|
191
|
+
</div>
|
|
192
|
+
)}
|
|
193
|
+
|
|
194
|
+
{/* No workspace yet */}
|
|
195
|
+
{isExpanded && ws && ws.agents.length === 0 && ws.id === '' && (
|
|
196
|
+
<div className="ml-6 py-1">
|
|
197
|
+
<button
|
|
198
|
+
onClick={(e) => {
|
|
199
|
+
e.stopPropagation();
|
|
200
|
+
onSelectProject(project);
|
|
201
|
+
}}
|
|
202
|
+
className="text-[9px] text-[var(--text-secondary)] hover:text-[var(--accent)]"
|
|
203
|
+
>
|
|
204
|
+
+ Create workspace
|
|
205
|
+
</button>
|
|
206
|
+
</div>
|
|
207
|
+
)}
|
|
208
|
+
|
|
209
|
+
{/* Empty workspace */}
|
|
210
|
+
{isExpanded && ws && ws.agents.length === 0 && ws.id !== '' && (
|
|
211
|
+
<div className="ml-6 py-1 text-[9px] text-[var(--text-secondary)]">
|
|
212
|
+
No agents yet
|
|
213
|
+
</div>
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
})}
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
}
|