@aion0/forge 0.5.43 → 0.5.44
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/RELEASE_NOTES.md +14 -5
- package/components/Dashboard.tsx +14 -0
- package/intellij-plugin/README.md +53 -0
- package/intellij-plugin/build.gradle.kts +60 -0
- package/intellij-plugin/gradle/gradle-daemon-jvm.properties +12 -0
- package/intellij-plugin/gradle.properties +9 -0
- package/intellij-plugin/publish.sh +78 -0
- package/intellij-plugin/settings.gradle.kts +7 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/action/LoginAction.kt +49 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/action/LogoutAction.kt +18 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/action/OpenWebUIAction.kt +13 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/action/SwitchConnectionAction.kt +26 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/api/ForgeClient.kt +115 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/auth/Auth.kt +31 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/connection/ConnectionManager.kt +95 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/settings/ForgeConfigurable.kt +81 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/DocsView.kt +99 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/ForgeStatusBarWidgetFactory.kt +94 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/ForgeToolWindowFactory.kt +27 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/ForgeTreeView.kt +176 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/Helpers.kt +48 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/PipelinesView.kt +226 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/TerminalsView.kt +309 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/TreeNodeData.kt +33 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/WorkspacesView.kt +166 -0
- package/intellij-plugin/src/main/resources/META-INF/plugin.xml +88 -0
- package/intellij-plugin/src/main/resources/icons/forge.svg +3 -0
- package/lib/agents/index.ts +1 -1
- package/lib/help-docs/00-overview.md +1 -0
- package/lib/help-docs/13-ide-plugins.md +90 -0
- package/lib/help-docs/CLAUDE.md +3 -0
- package/package.json +1 -1
- package/vscode-extension/.vscodeignore +11 -0
- package/vscode-extension/README.md +48 -0
- package/vscode-extension/media/icon.png +0 -0
- package/vscode-extension/media/icon.svg +3 -0
- package/vscode-extension/package-lock.json +4046 -0
- package/vscode-extension/package.json +514 -0
- package/vscode-extension/publish.sh +49 -0
- package/vscode-extension/src/api/client.ts +217 -0
- package/vscode-extension/src/auth/auth.ts +32 -0
- package/vscode-extension/src/commands/auth.ts +44 -0
- package/vscode-extension/src/commands/connection.ts +113 -0
- package/vscode-extension/src/commands/docs.ts +40 -0
- package/vscode-extension/src/commands/pipeline.ts +103 -0
- package/vscode-extension/src/commands/server.ts +50 -0
- package/vscode-extension/src/commands/smith.ts +112 -0
- package/vscode-extension/src/commands/task.ts +43 -0
- package/vscode-extension/src/commands/terminal.ts +279 -0
- package/vscode-extension/src/commands/workspace.ts +138 -0
- package/vscode-extension/src/connection/manager.ts +80 -0
- package/vscode-extension/src/docs/fs-provider.ts +94 -0
- package/vscode-extension/src/docs/result-provider.ts +33 -0
- package/vscode-extension/src/docs/transport.ts +22 -0
- package/vscode-extension/src/extension.ts +314 -0
- package/vscode-extension/src/statusbar.ts +70 -0
- package/vscode-extension/src/terminal/pseudoterm.ts +123 -0
- package/vscode-extension/src/views/docs.ts +145 -0
- package/vscode-extension/src/views/pipelines.ts +222 -0
- package/vscode-extension/src/views/terminals.ts +91 -0
- package/vscode-extension/src/views/workspaces.ts +243 -0
- package/vscode-extension/tsconfig.json +16 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
package com.aion0.forge.ui.toolwindow
|
|
2
|
+
|
|
3
|
+
import com.aion0.forge.api.ForgeClient
|
|
4
|
+
import com.intellij.icons.AllIcons
|
|
5
|
+
import com.intellij.openapi.actionSystem.AnAction
|
|
6
|
+
import com.intellij.openapi.actionSystem.AnActionEvent
|
|
7
|
+
import com.intellij.openapi.project.Project
|
|
8
|
+
import com.intellij.openapi.ui.Messages
|
|
9
|
+
import org.jetbrains.plugins.terminal.ShellTerminalWidget
|
|
10
|
+
import org.jetbrains.plugins.terminal.TerminalView
|
|
11
|
+
import javax.swing.tree.DefaultMutableTreeNode
|
|
12
|
+
|
|
13
|
+
class WorkspacesView(project: Project) : ForgeTreeView(project) {
|
|
14
|
+
|
|
15
|
+
/** Cache of open smith terminals keyed by tmux session name. Re-clicking a smith
|
|
16
|
+
* focuses the existing terminal tab instead of spawning a duplicate. */
|
|
17
|
+
private val openedTerminals = mutableMapOf<String, ShellTerminalWidget>()
|
|
18
|
+
|
|
19
|
+
override fun rootLabel() = "workspaces"
|
|
20
|
+
|
|
21
|
+
override fun reload(): List<DefaultMutableTreeNode> {
|
|
22
|
+
val r = ForgeClient.get().request("/api/workspace")
|
|
23
|
+
if (r.status == 401 || r.status == 403) return listOf(DefaultMutableTreeNode(TreeNodeData.Hint("🔑 Tools → Forge: Login")))
|
|
24
|
+
if (!r.ok || r.data == null || !r.data.isJsonArray) {
|
|
25
|
+
return listOf(DefaultMutableTreeNode(TreeNodeData.Hint("⚠ ${r.error ?: "Not connected"}")))
|
|
26
|
+
}
|
|
27
|
+
val arr = r.data.asJsonArray
|
|
28
|
+
if (arr.size() == 0) return listOf(DefaultMutableTreeNode(TreeNodeData.Hint("No workspaces yet")))
|
|
29
|
+
val sorted = arr.toList().sortedBy { it.asJsonObject.get("projectName")?.asString ?: "" }
|
|
30
|
+
return sorted.mapNotNull { el ->
|
|
31
|
+
val ws = el.asJsonObject
|
|
32
|
+
val id = ws.get("id")?.asString ?: return@mapNotNull null
|
|
33
|
+
val name = ws.get("projectName")?.asString ?: id
|
|
34
|
+
val ar = ForgeClient.get().request("/api/workspace/$id/agents")
|
|
35
|
+
val agents = ar.data?.asJsonObject?.getAsJsonArray("agents")
|
|
36
|
+
val states = ar.data?.asJsonObject?.getAsJsonObject("states")
|
|
37
|
+
val daemon = ar.data?.asJsonObject?.get("daemonActive")?.asBoolean ?: false
|
|
38
|
+
val mark = if (daemon) "🟢" else "○"
|
|
39
|
+
val node = DefaultMutableTreeNode(TreeNodeData.Workspace("$mark $name (${agents?.size() ?: 0} smiths)", id, name, daemon))
|
|
40
|
+
agents?.forEach { aEl ->
|
|
41
|
+
val a = aEl.asJsonObject
|
|
42
|
+
val aId = a.get("id")?.asString ?: return@forEach
|
|
43
|
+
val label = a.get("label")?.asString ?: aId
|
|
44
|
+
val icon = a.get("icon")?.asString ?: "🤖"
|
|
45
|
+
val s = states?.getAsJsonObject(aId)
|
|
46
|
+
val task = s?.get("taskStatus")?.asString ?: "idle"
|
|
47
|
+
val smith = s?.get("smithStatus")?.asString ?: "down"
|
|
48
|
+
val paused = s?.get("paused")?.asBoolean == true
|
|
49
|
+
val tmux = s?.get("tmuxSession")?.asString
|
|
50
|
+
val statusEmoji = when {
|
|
51
|
+
paused -> "⏸"
|
|
52
|
+
smith == "down" -> "○"
|
|
53
|
+
smith == "starting"-> "◐"
|
|
54
|
+
task == "running" -> "▶"
|
|
55
|
+
task == "failed" -> "✕"
|
|
56
|
+
task == "done" -> "✓"
|
|
57
|
+
else -> "·"
|
|
58
|
+
}
|
|
59
|
+
node.add(DefaultMutableTreeNode(
|
|
60
|
+
TreeNodeData.Smith("$statusEmoji $icon $label ($task)", id, aId, label, task, paused, tmux),
|
|
61
|
+
))
|
|
62
|
+
}
|
|
63
|
+
node
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
override fun onDoubleClick(data: TreeNodeData, node: DefaultMutableTreeNode) {
|
|
68
|
+
when (data) {
|
|
69
|
+
is TreeNodeData.Smith -> openSmithTerminal(data)
|
|
70
|
+
else -> {}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
override fun contextActions(data: TreeNodeData, node: DefaultMutableTreeNode): List<AnAction> = when (data) {
|
|
75
|
+
is TreeNodeData.Workspace -> if (data.daemonActive) {
|
|
76
|
+
listOf(
|
|
77
|
+
act("Stop Daemon", AllIcons.Actions.Suspend) { confirmAndDoWs(data.workspaceId, "stop_daemon", "Stop the workspace daemon? Running smiths will be terminated.") },
|
|
78
|
+
act("Restart Daemon",AllIcons.Actions.Restart) { runApi(project, "Restart daemon", { restartDaemon(data.workspaceId) }) { refresh() } },
|
|
79
|
+
)
|
|
80
|
+
} else {
|
|
81
|
+
listOf(
|
|
82
|
+
act("Start Daemon", AllIcons.Actions.Execute) { runApi(project, "Start daemon", { wsAction(data.workspaceId, "start_daemon") }) { refresh() } },
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
is TreeNodeData.Smith -> buildList {
|
|
86
|
+
add(act("Open Terminal", AllIcons.Debugger.Console) { openSmithTerminal(data) })
|
|
87
|
+
add(act("Send Message", AllIcons.General.Balloon) { promptSendMessage(data) })
|
|
88
|
+
if (data.paused) {
|
|
89
|
+
add(act("Resume", AllIcons.Actions.Execute) { runApi(project, "Resume ${data.agentLabel}", { wsAction(data.workspaceId, "resume", mapOf("agentId" to data.agentId)) }) { refresh() } })
|
|
90
|
+
} else {
|
|
91
|
+
add(act("Pause", AllIcons.Actions.Suspend) { runApi(project, "Pause ${data.agentLabel}", { wsAction(data.workspaceId, "pause", mapOf("agentId" to data.agentId)) }) { refresh() } })
|
|
92
|
+
}
|
|
93
|
+
if (data.taskStatus == "running") {
|
|
94
|
+
add(act("Mark Done", null) { runApi(project, "Mark done", { wsAction(data.workspaceId, "mark_done", mapOf("agentId" to data.agentId, "notify" to true)) }) { refresh() } })
|
|
95
|
+
add(act("Mark Failed", null) { runApi(project, "Mark failed", { wsAction(data.workspaceId, "mark_failed", mapOf("agentId" to data.agentId, "notify" to true)) }) { refresh() } })
|
|
96
|
+
add(act("Mark Idle", null) { runApi(project, "Mark idle", { wsAction(data.workspaceId, "mark_done", mapOf("agentId" to data.agentId, "notify" to false)) }) { refresh() } })
|
|
97
|
+
}
|
|
98
|
+
if (data.taskStatus == "failed") {
|
|
99
|
+
add(act("Retry", AllIcons.Actions.Refresh) { runApi(project, "Retry ${data.agentLabel}", { wsAction(data.workspaceId, "retry", mapOf("agentId" to data.agentId)) }) { refresh() } })
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else -> emptyList()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private fun confirmAndDoWs(wsId: String, action: String, prompt: String) {
|
|
106
|
+
val r = Messages.showYesNoDialog(project, prompt, "Forge", Messages.getQuestionIcon())
|
|
107
|
+
if (r == Messages.YES) runApi(project, action, { wsAction(wsId, action) }) { refresh() }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private fun restartDaemon(wsId: String): com.aion0.forge.api.ApiResult {
|
|
111
|
+
val stop = wsAction(wsId, "stop_daemon")
|
|
112
|
+
if (!stop.ok) return stop
|
|
113
|
+
Thread.sleep(800)
|
|
114
|
+
return wsAction(wsId, "start_daemon")
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Open an IDE terminal and run `tmux attach -t <session>` — works for
|
|
118
|
+
* local forge. Remote forge would need a WebSocket-bridged JediTerm
|
|
119
|
+
* session (TODO). Reuses an existing terminal tab if one was already
|
|
120
|
+
* opened for this smith. */
|
|
121
|
+
private fun openSmithTerminal(smith: TreeNodeData.Smith) {
|
|
122
|
+
val res = ForgeClient.get().request(
|
|
123
|
+
"/api/workspace/${smith.workspaceId}/agents",
|
|
124
|
+
method = "POST",
|
|
125
|
+
body = mapOf("action" to "open_terminal", "agentId" to smith.agentId),
|
|
126
|
+
)
|
|
127
|
+
val tmux = res.data?.asJsonObject?.get("tmuxSession")?.asString ?: smith.tmuxSession
|
|
128
|
+
if (tmux.isNullOrBlank()) {
|
|
129
|
+
notify(project, "Forge: smith ${smith.agentLabel} has no tmux session yet — start the daemon first.", com.intellij.notification.NotificationType.WARNING)
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
val existing = openedTerminals[tmux]
|
|
133
|
+
if (existing != null && !com.intellij.openapi.util.Disposer.isDisposed(existing)) {
|
|
134
|
+
// Surface the existing tab in the Terminal tool window.
|
|
135
|
+
val tw = com.intellij.openapi.wm.ToolWindowManager.getInstance(project).getToolWindow("Terminal")
|
|
136
|
+
tw?.activate(null)
|
|
137
|
+
existing.requestFocusInWindow()
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
val terminalView = TerminalView.getInstance(project)
|
|
141
|
+
val widget = terminalView.createLocalShellWidget(project.basePath ?: System.getProperty("user.home"), "forge: ${smith.agentLabel}")
|
|
142
|
+
openedTerminals[tmux] = widget
|
|
143
|
+
com.intellij.openapi.util.Disposer.register(widget) { openedTerminals.remove(tmux) }
|
|
144
|
+
// Force UTF-8 + 256-color terminfo so the tmux UI doesn't render as garbled
|
|
145
|
+
// box-drawing characters in JediTerm. `-u` opts into UTF-8, `-2` forces 256-color.
|
|
146
|
+
widget.executeCommand("TERM=xterm-256color tmux -2 -u attach -t \"$tmux\" || TERM=xterm-256color tmux -2 -u new -A -s \"$tmux\"")
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private fun promptSendMessage(smith: TreeNodeData.Smith) {
|
|
150
|
+
val text = Messages.showMultilineInputDialog(
|
|
151
|
+
project,
|
|
152
|
+
"Send message to ${smith.agentLabel}",
|
|
153
|
+
"Forge: Send Message",
|
|
154
|
+
"",
|
|
155
|
+
null, null,
|
|
156
|
+
) ?: return
|
|
157
|
+
if (text.isBlank()) return
|
|
158
|
+
runApi(project, "Send message to ${smith.agentLabel}",
|
|
159
|
+
{ wsAction(smith.workspaceId, "message", mapOf("agentId" to smith.agentId, "content" to text)) },
|
|
160
|
+
) { refresh() }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private fun act(name: String, icon: javax.swing.Icon?, run: () -> Unit) = object : AnAction(name, null, icon) {
|
|
164
|
+
override fun actionPerformed(e: AnActionEvent) = run()
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<idea-plugin>
|
|
2
|
+
<id>com.aion0.forge</id>
|
|
3
|
+
<name>Forge Vibe Coding</name>
|
|
4
|
+
<version>0.1.0</version>
|
|
5
|
+
<vendor email="liuzhen1984@gmail.com" url="https://github.com/aiwatching/forge">aion0</vendor>
|
|
6
|
+
|
|
7
|
+
<description><![CDATA[
|
|
8
|
+
<p>Drive your <a href="https://github.com/aiwatching/forge">Forge</a> instance from
|
|
9
|
+
the IDE — workspaces, agents, terminals, pipelines and docs in one tool window.</p>
|
|
10
|
+
|
|
11
|
+
<p><b>⚠ Requires Forge running locally.</b> Install once:</p>
|
|
12
|
+
<pre>npm install -g @aion0/forge
|
|
13
|
+
forge server start</pre>
|
|
14
|
+
<p>Default port <code>8403</code>. Configure remote forges under
|
|
15
|
+
<b>Settings → Tools → Forge</b>.</p>
|
|
16
|
+
|
|
17
|
+
<h3>Features</h3>
|
|
18
|
+
<ul>
|
|
19
|
+
<li>Workspaces — daemon control, multi-agent smiths, tmux attach, pause/resume/retry</li>
|
|
20
|
+
<li>Terminals — per-project claude sessions, double-click to resume, right-click → Open With for any agent</li>
|
|
21
|
+
<li>Pipelines — bindings + run details with per-node prompt / result / diff viewer</li>
|
|
22
|
+
<li>Docs — open files in editor, "open terminal here"</li>
|
|
23
|
+
<li>Multi-connection — local + remote forges with status-bar switcher</li>
|
|
24
|
+
</ul>
|
|
25
|
+
]]></description>
|
|
26
|
+
|
|
27
|
+
<change-notes><![CDATA[
|
|
28
|
+
<h3>0.1.0</h3>
|
|
29
|
+
<ul>
|
|
30
|
+
<li>Initial scaffold: tool window shell, settings page, login flow, multi-connection support.</li>
|
|
31
|
+
</ul>
|
|
32
|
+
]]></change-notes>
|
|
33
|
+
|
|
34
|
+
<depends>com.intellij.modules.platform</depends>
|
|
35
|
+
<depends>org.jetbrains.plugins.terminal</depends>
|
|
36
|
+
|
|
37
|
+
<extensions defaultExtensionNs="com.intellij">
|
|
38
|
+
<!-- Persisted application-level state (connections + active connection). -->
|
|
39
|
+
<applicationService serviceImplementation="com.aion0.forge.connection.ConnectionManager"/>
|
|
40
|
+
<applicationService serviceImplementation="com.aion0.forge.api.ForgeClient"/>
|
|
41
|
+
<applicationService serviceImplementation="com.aion0.forge.auth.Auth"/>
|
|
42
|
+
|
|
43
|
+
<!-- Tool window: lives on the right side, contains tabs for each section. -->
|
|
44
|
+
<toolWindow
|
|
45
|
+
id="Forge"
|
|
46
|
+
anchor="left"
|
|
47
|
+
secondary="true"
|
|
48
|
+
icon="/icons/forge.svg"
|
|
49
|
+
factoryClass="com.aion0.forge.ui.toolwindow.ForgeToolWindowFactory"/>
|
|
50
|
+
|
|
51
|
+
<!-- Settings page (Preferences → Tools → Forge). -->
|
|
52
|
+
<applicationConfigurable
|
|
53
|
+
parentId="tools"
|
|
54
|
+
instance="com.aion0.forge.settings.ForgeConfigurable"
|
|
55
|
+
id="com.aion0.forge.settings.ForgeConfigurable"
|
|
56
|
+
displayName="Forge"/>
|
|
57
|
+
|
|
58
|
+
<!-- Status bar widget — connection state + click-to-switch. -->
|
|
59
|
+
<statusBarWidgetFactory
|
|
60
|
+
id="com.aion0.forge.statusbar"
|
|
61
|
+
implementation="com.aion0.forge.ui.toolwindow.ForgeStatusBarWidgetFactory"
|
|
62
|
+
order="last"/>
|
|
63
|
+
|
|
64
|
+
<notificationGroup id="Forge" displayType="BALLOON"/>
|
|
65
|
+
</extensions>
|
|
66
|
+
|
|
67
|
+
<actions>
|
|
68
|
+
<group id="com.aion0.forge.actions" text="Forge" popup="true">
|
|
69
|
+
<action id="com.aion0.forge.action.LoginAction"
|
|
70
|
+
class="com.aion0.forge.action.LoginAction"
|
|
71
|
+
text="Forge: Login"
|
|
72
|
+
description="Authenticate against the active Forge server."/>
|
|
73
|
+
<action id="com.aion0.forge.action.LogoutAction"
|
|
74
|
+
class="com.aion0.forge.action.LogoutAction"
|
|
75
|
+
text="Forge: Logout"
|
|
76
|
+
description="Clear the saved token for the active connection."/>
|
|
77
|
+
<action id="com.aion0.forge.action.SwitchConnectionAction"
|
|
78
|
+
class="com.aion0.forge.action.SwitchConnectionAction"
|
|
79
|
+
text="Forge: Switch Connection…"
|
|
80
|
+
description="Pick a different Forge server."/>
|
|
81
|
+
<action id="com.aion0.forge.action.OpenWebUIAction"
|
|
82
|
+
class="com.aion0.forge.action.OpenWebUIAction"
|
|
83
|
+
text="Forge: Open Web UI"
|
|
84
|
+
description="Open the Forge web UI in the default browser."/>
|
|
85
|
+
<add-to-group group-id="ToolsMenu" anchor="last"/>
|
|
86
|
+
</group>
|
|
87
|
+
</actions>
|
|
88
|
+
</idea-plugin>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="currentColor">
|
|
2
|
+
<path d="M12 2L3 7v6c0 5 3.5 9.5 9 11 5.5-1.5 9-6 9-11V7l-9-5zm0 4.5l5 2.8v3.4l-5-2.8-5 2.8V9.3l5-2.8zm-5 6.4l5 2.8v5.6c-3.4-1.4-5-4.6-5-7.6v-.8zm10 .8c0 3-1.6 6.2-5 7.6v-5.6l5-2.8v.8z"/>
|
|
3
|
+
</svg>
|
package/lib/agents/index.ts
CHANGED
|
@@ -209,7 +209,7 @@ export function resolveTerminalLaunch(agentId?: string): TerminalLaunchInfo {
|
|
|
209
209
|
|
|
210
210
|
// Determine CLI command and capabilities from cliType
|
|
211
211
|
const cliMap: Record<string, { cmd: string; session: boolean; resume: string }> = {
|
|
212
|
-
'claude-code': { cmd: 'claude', session: true, resume: '
|
|
212
|
+
'claude-code': { cmd: 'claude', session: true, resume: '--resume' },
|
|
213
213
|
'codex': { cmd: 'codex', session: false, resume: '' },
|
|
214
214
|
'aider': { cmd: 'aider', session: false, resume: '' },
|
|
215
215
|
'generic': { cmd: agentCfg.path || agentId || 'claude', session: false, resume: '' },
|
|
@@ -17,6 +17,7 @@ Forge is a self-hosted Vibe Coding platform for Claude Code. It provides a brows
|
|
|
17
17
|
| **Remote Access** | One-click Cloudflare tunnel for remote browsing |
|
|
18
18
|
| **GitHub Issue Auto-fix** | Scan issues, auto-fix, create PRs |
|
|
19
19
|
| **Memory (optional)** | Code graph + knowledge via `@aion0/temper` MCP server |
|
|
20
|
+
| **IDE Plugins** | First-party VSCode extension and IntelliJ plugin — drive workspaces, agents, terminals, pipelines and docs from inside the editor (see `13-ide-plugins.md`) |
|
|
20
21
|
|
|
21
22
|
## Quick Start
|
|
22
23
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# IDE Plugins
|
|
2
|
+
|
|
3
|
+
Forge ships with two first-party IDE plugins that mirror the web UI's feature set so you can drive workspaces, agent terminals, pipelines and docs without leaving the editor. Both are **thin clients** — they require a Forge server running locally (or reachable over a tunnel).
|
|
4
|
+
|
|
5
|
+
| | VSCode extension | IntelliJ plugin |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| Marketplace ID | `aion0.forge-vibecoding` | `Forge Vibe Coding` (`com.aion0.forge`) |
|
|
8
|
+
| Install | VSCode → Extensions → search `Forge Vibe Coding` | IDE → Settings → Plugins → Marketplace → search `Forge Vibe Coding` |
|
|
9
|
+
| Source | `vscode-extension/` | `intellij-plugin/` |
|
|
10
|
+
| Min IDE | VSCode ≥ 1.80 | IntelliJ Platform ≥ 2024.1 (build 241) |
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
The plugins do **not** ship Forge — install it once first:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g @aion0/forge
|
|
18
|
+
forge server start
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Default port `8403`. The plugins auto-detect `http://localhost:8403`. For remote forges (over Cloudflare tunnel or LAN), add a connection in plugin settings (see below).
|
|
22
|
+
|
|
23
|
+
## Tool Window / Sidebar Layout
|
|
24
|
+
|
|
25
|
+
Both plugins expose four tabs:
|
|
26
|
+
|
|
27
|
+
| Tab | Shows | Right-click actions | Double-click |
|
|
28
|
+
|---|---|---|---|
|
|
29
|
+
| **Workspaces** | Forge workspaces with daemon status (🟢/○) and per-smith status emoji (▶ running / ⏸ paused / ✓ done / ✕ failed / ◐ starting) | Workspace: start / stop / restart daemon. Smith: open terminal, send message, pause/resume, mark done/failed/idle, retry. | Smith → attach to its tmux session in an IDE terminal |
|
|
30
|
+
| **Terminals** | Each forge project as a folder with its claude sessions (★ = bound/pinned default). | Project: **Open With ▸** submenu of every configured agent (claude/codex/aider/...) — fresh launch; New Session… (pick agent); Plain Terminal Here. Session: Resume; Resume With… submenu; Pin as Default Session. | Session row resumes that exact session via `claude --resume <id>` |
|
|
31
|
+
| **Pipelines** | Forge projects with their pipeline bindings (⚙ enabled / ⊘ disabled) and recent runs (▶/✓/✕/⊘). | Project: Add Pipeline…; Binding: Trigger Now, Enable/Disable, Remove; Run: Show Nodes; Node: Show Result. | Binding: trigger; Run: expand to see nodes; Node: open prompt/result/diff/log as a markdown buffer |
|
|
32
|
+
| **Docs** | Configured doc roots → file/dir tree. | Dir: Open Terminal Here (runs claude); File: Open. | File: open in IDE editor |
|
|
33
|
+
|
|
34
|
+
## Multi-Connection (Local + Remote forges)
|
|
35
|
+
|
|
36
|
+
Both plugins support multiple Forge servers — useful when you have one Forge running locally for `~/.forge` and a second running on an office Mac mini exposed via tunnel.
|
|
37
|
+
|
|
38
|
+
**VSCode**: edit `forge.connections` in settings.json:
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"forge.connections": [
|
|
42
|
+
{ "name": "Local", "serverUrl": "http://localhost:8403", "terminalUrl": "ws://localhost:8404" },
|
|
43
|
+
{ "name": "Office", "serverUrl": "https://forge-office.trycloudflare.com", "terminalUrl": "wss://forge-office.trycloudflare.com" }
|
|
44
|
+
],
|
|
45
|
+
"forge.activeConnection": "Local"
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
Click the status-bar entry (bottom right, `Forge: <name>`) to switch.
|
|
49
|
+
|
|
50
|
+
**IntelliJ**: Settings → Tools → Forge → Connections list. Status-bar widget toggles active connection.
|
|
51
|
+
|
|
52
|
+
Tokens are stored per-connection: VSCode uses `SecretStorage`, IntelliJ uses `PasswordSafe`. `Forge: Login` (command palette / Tools menu) prompts for the admin password and caches the bearer token.
|
|
53
|
+
|
|
54
|
+
## How agent terminals work
|
|
55
|
+
|
|
56
|
+
When you double-click a session row or pick `Open With ▸ <agent>`:
|
|
57
|
+
|
|
58
|
+
1. Plugin calls `GET /api/agents?resolve=<agentId>` → gets `cliCmd`, `cliType`, `supportsSession`, `env`, `model`.
|
|
59
|
+
2. For specific-session resume: `claude --resume <sid>` (forced regardless of API's `resumeFlag` — `-c` is wrong for specific resume).
|
|
60
|
+
3. Profile env vars are forwarded to the spawned process.
|
|
61
|
+
4. `model` is passed as `--model <name>` for claude-code agents (so a "sonnet" profile actually runs sonnet).
|
|
62
|
+
5. **IntelliJ** spawns the agent CLI directly as the pty's primary process (`LocalTerminalDirectRunner` subclass with `enableShellIntegration = false` + `configureStartupOptions` override) — no shell, no `executeCommand` race. The user's login shell (`$SHELL -l`) is wrapped around it so `.zprofile` / `.bash_profile` is sourced for PATH.
|
|
63
|
+
6. **VSCode** uses the existing terminal-server WebSocket (`forge.terminalUrl`) to attach.
|
|
64
|
+
|
|
65
|
+
Workspace smith terminals attach to a pre-existing tmux session via `tmux -2 -u attach -t <session>` (UTF-8 + 256-color forced, otherwise JediTerm/xterm.js render boxes wrong).
|
|
66
|
+
|
|
67
|
+
## Releasing new versions (maintainers)
|
|
68
|
+
|
|
69
|
+
Each plugin has a `publish.sh` in its directory:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# VSCode — needs `vsce login aion0` cached, or $VSCE_PAT env var
|
|
73
|
+
cd vscode-extension
|
|
74
|
+
./publish.sh patch # bump 0.2.x → 0.2.(x+1) and publish
|
|
75
|
+
|
|
76
|
+
# IntelliJ — needs $JETBRAINS_MARKETPLACE_TOKEN env var
|
|
77
|
+
cd intellij-plugin
|
|
78
|
+
./publish.sh patch
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
VSCode goes live in 1–2 minutes. IntelliJ goes through human moderation: 1–3 business days for the first publish, hours-to-1-day for subsequent updates of an already-approved plugin.
|
|
82
|
+
|
|
83
|
+
## Troubleshooting
|
|
84
|
+
|
|
85
|
+
- **"Not connected" / "401" in tree** — Forge server isn't running or the token expired. Run `Forge: Login` (or `forge server start` if the server is down).
|
|
86
|
+
- **Terminal opens but agent CLI fails** ("`claude: command not found`") — IDE inherited a stripped PATH. The IntelliJ plugin spawns under `$SHELL -l -c …` to source `.zprofile`/`.bash_profile`; if your PATH is set in `.zshrc` only (not `.zprofile`), move it.
|
|
87
|
+
- **Smith terminal shows garbled output** — make sure tmux ≥ 3.0 and the IDE terminal supports UTF-8 + 256-color. The plugin already passes `tmux -2 -u attach`; if it still looks broken, check `$LANG` is `*.UTF-8`.
|
|
88
|
+
- **Picked a session, claude opens a different one** — Forge ≤ 0.5.43 had `resume: '-c'` hardcoded for claude (which is `--continue`, zero-arg). Upgrade Forge or pull the latest `lib/agents/index.ts`.
|
|
89
|
+
- **JetBrains plugin install fails with "incompatible build"** — verify your IDE is build 241+ (Help → About → Build #). The plugin's `sinceBuild = 241`.
|
|
90
|
+
- **Tree keeps collapsing folders I expanded** — fixed in IntelliJ plugin v0.1.17 (full path expansion is preserved across the 5-second poll, not just top-level).
|
package/lib/help-docs/CLAUDE.md
CHANGED
|
@@ -43,6 +43,7 @@ The token is valid for 24 hours. Store it in a variable and reuse for all API ca
|
|
|
43
43
|
| `10-troubleshooting.md` | Common issues and solutions |
|
|
44
44
|
| `11-workspace.md` | Workspace (Forge Smiths) — multi-agent orchestration, daemon, message bus, profiles |
|
|
45
45
|
| `12-usage.md` | Token usage analytics — charts, heatmap, cost estimation, by model/project/source |
|
|
46
|
+
| `13-ide-plugins.md` | VSCode extension + IntelliJ plugin — install, tabs, multi-connection, agent terminal launching |
|
|
46
47
|
|
|
47
48
|
## Matching questions to docs
|
|
48
49
|
|
|
@@ -64,3 +65,5 @@ The token is valid for 24 hours. Store it in a variable and reuse for all API ca
|
|
|
64
65
|
- Usage/cost/tokens/spending/billing/analytics → `12-usage.md`
|
|
65
66
|
- Terminal dock/float/mouse toggle/reconnect → `07-projects.md` + `11-workspace.md`
|
|
66
67
|
- Sidebar collapse/project tabs/favorites → `07-projects.md`
|
|
68
|
+
- VSCode/IntelliJ/IDE plugin/extension/marketplace → `13-ide-plugins.md`
|
|
69
|
+
- vsce/vsix/JetBrains marketplace publish → `13-ide-plugins.md`
|
package/package.json
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Forge VSCode Extension
|
|
2
|
+
|
|
3
|
+
Native VSCode integration for [Forge](https://github.com/aiwatching/forge).
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
- **Sidebar** — workspaces, smiths, active terminals, and background tasks at a glance
|
|
8
|
+
- **Native terminals** — attach to or create Forge tmux sessions in VSCode's terminal panel (uses VSCode's own Pseudoterminal API; sessions show up next to your regular terminals)
|
|
9
|
+
- **Send selection** — pipe selected code to a running Forge terminal
|
|
10
|
+
- **Notifications** — smith bell events surface as VSCode notifications
|
|
11
|
+
- **Status bar** — connection indicator + quick command access
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
14
|
+
|
|
15
|
+
1. Install Forge CLI: `npm install -g @aion0/forge`
|
|
16
|
+
2. Start the server: `forge server start`
|
|
17
|
+
3. Install this extension (VSCode → "Install from VSIX..." or marketplace)
|
|
18
|
+
4. Cmd/Ctrl+Shift+P → `Forge: Login` → enter your admin password
|
|
19
|
+
|
|
20
|
+
The token is stored in VSCode's SecretStorage and reused across sessions.
|
|
21
|
+
|
|
22
|
+
## Settings
|
|
23
|
+
|
|
24
|
+
| Key | Default | Description |
|
|
25
|
+
|-----|---------|-------------|
|
|
26
|
+
| `forge.serverUrl` | `http://localhost:8403` | Forge HTTP server |
|
|
27
|
+
| `forge.terminalUrl` | `ws://localhost:8404` | Terminal WebSocket |
|
|
28
|
+
| `forge.autoStart` | `false` | Spawn `forge server start` on activation if unreachable |
|
|
29
|
+
| `forge.notifications.enabled` | `true` | Smith-bell → VSCode notification |
|
|
30
|
+
| `forge.refreshInterval` | `5` | Tree refresh interval (seconds) |
|
|
31
|
+
|
|
32
|
+
## Local install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cd vscode-extension
|
|
36
|
+
npm install
|
|
37
|
+
npm run package # produces forge-vscode.vsix
|
|
38
|
+
code --install-extension forge-vscode.vsix
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Develop
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
cd vscode-extension
|
|
45
|
+
npm install
|
|
46
|
+
npm run watch # in one terminal
|
|
47
|
+
# In VSCode: F5 to launch Extension Development Host
|
|
48
|
+
```
|
|
Binary file
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
|
2
|
+
<path d="M12 2L3 7v6c0 5 3.5 9.5 9 11 5.5-1.5 9-6 9-11V7l-9-5zm0 4.5l5 2.8v3.4l-5-2.8-5 2.8V9.3l5-2.8zm-5 6.4l5 2.8v5.6c-3.4-1.4-5-4.6-5-7.6v-.8zm10 .8c0 3-1.6 6.2-5 7.6v-5.6l5-2.8v.8z"/>
|
|
3
|
+
</svg>
|