@aion0/forge 0.5.42 → 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 +16 -4
- package/components/Dashboard.tsx +22 -1
- package/components/WorkspaceView.tsx +15 -2
- 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/11-workspace.md +2 -1
- package/lib/help-docs/13-ide-plugins.md +90 -0
- package/lib/help-docs/CLAUDE.md +3 -0
- package/lib/workspace/orchestrator.ts +80 -7
- package/lib/workspace/persistence.ts +16 -0
- package/lib/workspace/types.ts +13 -0
- package/lib/workspace/watch-manager.ts +65 -24
- package/lib/workspace-standalone.ts +5 -9
- package/next-env.d.ts +1 -1
- 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
|
@@ -21,10 +21,18 @@ interface WatchSnapshot {
|
|
|
21
21
|
gitHash?: string;
|
|
22
22
|
commandOutput?: string;
|
|
23
23
|
logLineCount?: number; // last known line count in agent's logs.jsonl
|
|
24
|
-
|
|
24
|
+
/** Per-target last-seen taskStatusChangedAt. Watch fires when the target's
|
|
25
|
+
* current taskStatusChangedAt > the value recorded here. Updated only after
|
|
26
|
+
* a successful (non-defer) match-check. */
|
|
27
|
+
agentStatusLastSeen?: Record<string, number>;
|
|
25
28
|
sessionFileSize?: number; // last known file size of session JSONL (bytes)
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
/** Statuses that mean "still working — defer firing until it settles". */
|
|
32
|
+
function isBusyStatus(s: string | undefined): boolean {
|
|
33
|
+
return s === 'running' || s === 'starting';
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
interface WatchChange {
|
|
29
37
|
targetType: WatchTarget['type'];
|
|
30
38
|
description: string;
|
|
@@ -330,7 +338,7 @@ export class WatchManager extends EventEmitter {
|
|
|
330
338
|
constructor(
|
|
331
339
|
private workspaceId: string,
|
|
332
340
|
private projectPath: string,
|
|
333
|
-
private getAgents: () => Map<string, { config: WorkspaceAgentConfig; state: { smithStatus: string; taskStatus: string; mode
|
|
341
|
+
private getAgents: () => Map<string, { config: WorkspaceAgentConfig; state: { smithStatus: string; taskStatus: string; mode?: string; taskStatusChangedAt?: number } }>,
|
|
334
342
|
) {
|
|
335
343
|
super();
|
|
336
344
|
}
|
|
@@ -346,9 +354,7 @@ export class WatchManager extends EventEmitter {
|
|
|
346
354
|
|
|
347
355
|
/** Stop all watch loops */
|
|
348
356
|
stop(): void {
|
|
349
|
-
for (const
|
|
350
|
-
clearInterval(timer);
|
|
351
|
-
}
|
|
357
|
+
for (const timer of this.timers.values()) clearInterval(timer);
|
|
352
358
|
this.timers.clear();
|
|
353
359
|
console.log(`[watch] All watch loops stopped`);
|
|
354
360
|
}
|
|
@@ -392,7 +398,9 @@ export class WatchManager extends EventEmitter {
|
|
|
392
398
|
const now = Date.now();
|
|
393
399
|
const prev = this.snapshots.get(agentId) || { lastCheckTime: now };
|
|
394
400
|
const allChanges: WatchChange[] = [];
|
|
395
|
-
const newSnapshot: WatchSnapshot = {
|
|
401
|
+
const newSnapshot: WatchSnapshot = {
|
|
402
|
+
lastCheckTime: now,
|
|
403
|
+
};
|
|
396
404
|
|
|
397
405
|
for (const target of config.watch!.targets) {
|
|
398
406
|
switch (target.type) {
|
|
@@ -456,25 +464,58 @@ export class WatchManager extends EventEmitter {
|
|
|
456
464
|
break;
|
|
457
465
|
}
|
|
458
466
|
case 'agent_status': {
|
|
459
|
-
//
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
}
|
|
467
|
+
// Event-driven detection via taskStatusChangedAt timestamp on the target
|
|
468
|
+
// agent's state (orchestrator updates it on every real transition).
|
|
469
|
+
// The watch tick just compares timestamps — no fast-tick polling needed.
|
|
470
|
+
const targetAgentId = target.path;
|
|
471
|
+
if (!targetAgentId) break;
|
|
472
|
+
const agents = this.getAgents();
|
|
473
|
+
const targetEntry = agents.get(targetAgentId);
|
|
474
|
+
if (!targetEntry) break;
|
|
475
|
+
|
|
476
|
+
const cur = targetEntry.state.taskStatus;
|
|
477
|
+
const curChangedAt = targetEntry.state.taskStatusChangedAt || 0;
|
|
478
|
+
const lastSeenMap = newSnapshot.agentStatusLastSeen || (newSnapshot.agentStatusLastSeen = { ...(prev.agentStatusLastSeen || {}) });
|
|
479
|
+
|
|
480
|
+
// Initial run: record current changedAt so we don't fire on the
|
|
481
|
+
// existing state. Future transitions will advance the timestamp.
|
|
482
|
+
if (initialRun) {
|
|
483
|
+
lastSeenMap[targetAgentId] = curChangedAt;
|
|
484
|
+
console.log(`[watch] ${config.label}: agent_status baseline — ${targetEntry.config.label}=${cur} @ ${curChangedAt}`);
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const prevSeen = lastSeenMap[targetAgentId] || 0;
|
|
489
|
+
|
|
490
|
+
// No transition since last check.
|
|
491
|
+
if (curChangedAt <= prevSeen) {
|
|
492
|
+
console.log(`[watch] ${config.label}: ${targetEntry.config.label} no transition since last check (curAt=${curChangedAt})`);
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Target is mid-transition (still busy) — defer; do NOT update lastSeen
|
|
497
|
+
// so the next interval-tick can still see this as a pending transition.
|
|
498
|
+
if (isBusyStatus(cur)) {
|
|
499
|
+
console.log(`[watch] ${config.label}: ${targetEntry.config.label} = ${cur} (busy) → defer`);
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Settled state. Match pattern against final state only.
|
|
504
|
+
const pattern = target.pattern;
|
|
505
|
+
const matched = pattern ? !!cur.match(new RegExp(pattern, 'i')) : true;
|
|
506
|
+
if (matched) {
|
|
507
|
+
console.log(`[watch] ${config.label}: ${targetEntry.config.label} → ${cur} (changed since ${prevSeen}) pattern='${pattern || '(any)'}' → FIRE`);
|
|
508
|
+
allChanges.push({
|
|
509
|
+
targetType: 'agent_status',
|
|
510
|
+
description: `Agent ${targetEntry.config.label} settled to ${cur}${pattern ? ` (matched '${pattern}')` : ''}`,
|
|
511
|
+
files: [],
|
|
512
|
+
});
|
|
513
|
+
} else {
|
|
514
|
+
console.log(`[watch] ${config.label}: ${targetEntry.config.label} → ${cur} pattern='${pattern}' → no match`);
|
|
477
515
|
}
|
|
516
|
+
// Mark this transition as seen, so we don't fire again until the next
|
|
517
|
+
// real transition advances the timestamp.
|
|
518
|
+
lastSeenMap[targetAgentId] = curChangedAt;
|
|
478
519
|
break;
|
|
479
520
|
}
|
|
480
521
|
}
|
|
@@ -312,10 +312,9 @@ async function handleAgentsPost(id: string, body: any, res: ServerResponse): Pro
|
|
|
312
312
|
if (body.resolveOnly) {
|
|
313
313
|
let currentSessionId: string | null = null;
|
|
314
314
|
if (agentConfig.primary) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
} catch {}
|
|
315
|
+
// Auto-bind to project's latest existing session if no fixedSession yet,
|
|
316
|
+
// so the picker can offer "Current Session" on a fresh workspace.
|
|
317
|
+
currentSessionId = orch.resolvePrimaryFixedSession();
|
|
319
318
|
} else {
|
|
320
319
|
currentSessionId = agentConfig.boundSessionId || null;
|
|
321
320
|
}
|
|
@@ -744,11 +743,8 @@ async function handleSmith(id: string, body: any, res: ServerResponse): Promise<
|
|
|
744
743
|
// Get the primary agent's tmux session + project-level fixed session
|
|
745
744
|
const primary = orch.getPrimaryAgent();
|
|
746
745
|
if (!primary) return json(res, { ok: false, error: 'No primary agent configured' });
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
const { getFixedSession } = await import('./project-sessions.js');
|
|
750
|
-
fixedSessionId = getFixedSession(orch.projectPath) || null;
|
|
751
|
-
} catch {}
|
|
746
|
+
// Auto-bind to project's latest existing session if no fixedSession yet.
|
|
747
|
+
const fixedSessionId = orch.resolvePrimaryFixedSession();
|
|
752
748
|
return json(res, {
|
|
753
749
|
ok: true,
|
|
754
750
|
agentId: primary.config.id,
|
package/next-env.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
-
import "./.next/types/routes.d.ts";
|
|
3
|
+
import "./.next/dev/types/routes.d.ts";
|
|
4
4
|
|
|
5
5
|
// NOTE: This file should not be edited
|
|
6
6
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
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>
|