@aion0/forge 0.10.32 → 0.10.34
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 +8 -5
- package/app/api/activity/summary/route.ts +135 -0
- package/components/ActivityPanel.tsx +288 -0
- package/components/Dashboard.tsx +48 -29
- package/components/PipelineView.tsx +74 -3
- package/components/SkillsPanel.tsx +64 -29
- package/lib/chat/agent-loop.ts +68 -11
- package/lib/chat/build-memory-context.ts +36 -4
- package/lib/chat/llm/anthropic.ts +30 -1
- package/lib/chat/llm/openai.ts +12 -1
- package/lib/chat/llm/types.ts +11 -0
- package/lib/chat/session-store.ts +52 -1
- package/lib/help-docs/00-overview.md +14 -0
- package/lib/help-docs/06-skills.md +11 -1
- package/lib/help-docs/12-usage.md +1 -1
- package/lib/help-docs/24-watch.md +36 -11
- package/lib/watch/watch-runner.ts +76 -1
- package/package.json +1 -1
|
@@ -16,11 +16,21 @@ Both register as `/slash-command` in Claude Code.
|
|
|
16
16
|
|
|
17
17
|
## Install
|
|
18
18
|
|
|
19
|
-
1. Go to **
|
|
19
|
+
1. Go to **Marketplace** tab in Forge
|
|
20
20
|
2. Click **Sync** to fetch latest registry
|
|
21
21
|
3. Click **Install** on any skill → choose Global or specific project
|
|
22
22
|
4. Use in Claude Code with `/<skill-name>`
|
|
23
23
|
|
|
24
|
+
## Navigating the Marketplace
|
|
25
|
+
|
|
26
|
+
The toolbar has three group-scoped controls, ordered by usage frequency. Opening the Marketplace tab lands on **Pipelines** by default — the highest-traffic category.
|
|
27
|
+
|
|
28
|
+
- **Pipelines** (button) — Templates synced from `forge-workflow`. Default landing view.
|
|
29
|
+
- **Extensions ▾** — Connectors / Plugins / Crafts
|
|
30
|
+
- **Catalog ▾** — All / Skills / Commands / Local / Rules
|
|
31
|
+
|
|
32
|
+
The control whose value is currently active highlights in the accent color; the other two show their group label as a placeholder. Click any option to switch — no need to traverse a single long dropdown.
|
|
33
|
+
|
|
24
34
|
## Update
|
|
25
35
|
|
|
26
36
|
Skills with newer versions show a yellow "update" indicator. Click to update (checks for local modifications first).
|
|
@@ -4,7 +4,7 @@ Forge tracks Claude API token usage and estimated costs across all your projects
|
|
|
4
4
|
|
|
5
5
|
## Access
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Open the **user menu** (top-right `▾`) and click **Usage**. It sits next to Monitor and Login Status — the three periodic-check screens are grouped there so the top toolbar only carries at-a-glance signals.
|
|
8
8
|
|
|
9
9
|
## Data Source
|
|
10
10
|
|
|
@@ -47,25 +47,50 @@ Both use the same background machinery; you experience them identically.
|
|
|
47
47
|
## `start_watch` (for the assistant)
|
|
48
48
|
|
|
49
49
|
When you've just started a long job and have a tool that reports its
|
|
50
|
-
status, register a watch instead of polling in the conversation
|
|
50
|
+
status, register a watch instead of polling in the conversation. Two
|
|
51
|
+
poll forms are accepted:
|
|
52
|
+
|
|
53
|
+
**1. Connector tool** — `"<connector>.<tool>"`, e.g. `jenkins.get_build`:
|
|
51
54
|
|
|
52
55
|
```
|
|
53
56
|
start_watch({
|
|
54
|
-
poll: "jenkins.get_build",
|
|
57
|
+
poll: "jenkins.get_build",
|
|
55
58
|
poll_args: { job_path: "job/foo", build_number: 18 },
|
|
56
|
-
done_match: { path: "result", equals: "SUCCESS" },
|
|
59
|
+
done_match: { path: "result", equals: "SUCCESS" },
|
|
57
60
|
interval_sec: 60, timeout_sec: 1800,
|
|
58
|
-
message: "Build 18 finished: {poll.result}"
|
|
61
|
+
message: "Build 18 finished: {poll.result}"
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**2. Bare builtin name** — Forge's own status readers:
|
|
66
|
+
|
|
67
|
+
| Builtin | Pair with | done condition |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `get_pipeline_status` | `trigger_pipeline` → pipeline_id | `done_match={path:"status",equals:"done"}` (or `done_path:"terminal"`) |
|
|
70
|
+
| `get_task_status` | `dispatch_task` → task_id | `done_path:"terminal"` |
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
start_watch({
|
|
74
|
+
poll: "get_pipeline_status", // bare builtin, no prefix
|
|
75
|
+
poll_args: { pipeline_id: "a8e049a3" },
|
|
76
|
+
done_path: "terminal",
|
|
77
|
+
message: "Pipeline {poll.workflowName} finished: {poll.status}"
|
|
59
78
|
})
|
|
60
79
|
```
|
|
61
80
|
|
|
62
|
-
Then **stop** — do not keep calling
|
|
63
|
-
completion message arrives in chat; the user can cancel it from the
|
|
64
|
-
list. Pick `done_match`/`done_path` from a field you saw in the
|
|
65
|
-
tool's output (you usually called it once already). Queue/startup
|
|
66
|
-
(e.g. a build number 404 while still queued) are tolerated for a
|
|
67
|
-
Guards (max polls, timeout, lifetime, active cap) keep it from
|
|
68
|
-
away; worst case it times out and reports that.
|
|
81
|
+
Then **stop** — do not keep calling the status tool in the conversation.
|
|
82
|
+
A completion message arrives in chat; the user can cancel it from the
|
|
83
|
+
watch list. Pick `done_match`/`done_path` from a field you saw in the
|
|
84
|
+
status tool's output (you usually called it once already). Queue/startup
|
|
85
|
+
errors (e.g. a build number 404 while still queued) are tolerated for a
|
|
86
|
+
while. Guards (max polls, timeout, lifetime, active cap) keep it from
|
|
87
|
+
running away; worst case it times out and reports that.
|
|
88
|
+
|
|
89
|
+
### `_raw` fallback for non-JSON tools
|
|
90
|
+
|
|
91
|
+
The poll result is parsed as JSON. If the tool returns plain text (some
|
|
92
|
+
shell-protocol tools), Forge wraps it as `{_raw: "...full output..."}`
|
|
93
|
+
so you can still grep — e.g. `done_match={path:"_raw",contains:"DONE"}`.
|
|
69
94
|
|
|
70
95
|
## Limits
|
|
71
96
|
|
|
@@ -40,6 +40,45 @@ function parseResult(content: string): any {
|
|
|
40
40
|
try { return JSON.parse(content); } catch { return { _raw: content }; }
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/** Heuristic: spot common "this work is finished" shapes from a poll
|
|
44
|
+
* result, regardless of whether the connector author thought to set
|
|
45
|
+
* `terminal: true` or pre-declare done conditions. Walks well-known
|
|
46
|
+
* state-bearing fields (state / status / phase / result / done /
|
|
47
|
+
* finished / complete / completed) and matches their values against
|
|
48
|
+
* a curated vocabulary used across CI, Jenkins, k8s, generic build
|
|
49
|
+
* systems, etc.
|
|
50
|
+
* Returns { failure } when a hit is found, null otherwise. Intended
|
|
51
|
+
* to run AFTER user's explicit done_match/done_path, so a caller who
|
|
52
|
+
* configured "done when status == running" (rare but legal) still
|
|
53
|
+
* wins. */
|
|
54
|
+
function detectTerminalState(obj: any): { failure: boolean; source: string; value: string } | null {
|
|
55
|
+
if (!obj || typeof obj !== 'object') return null;
|
|
56
|
+
// Boolean done-ish flags
|
|
57
|
+
for (const f of ['done', 'finished', 'complete', 'completed']) {
|
|
58
|
+
if (truthy(obj[f])) return { failure: false, source: f, value: 'true' };
|
|
59
|
+
}
|
|
60
|
+
// State-bearing fields with a terminal vocabulary
|
|
61
|
+
const fields = ['state', 'status', 'phase', 'result', 'conclusion', 'lifecycle_state'];
|
|
62
|
+
const failureWords = new Set([
|
|
63
|
+
'failed', 'failure', 'error', 'errored', 'cancelled', 'canceled',
|
|
64
|
+
'aborted', 'killed', 'terminated', 'timeout', 'timed_out', 'rejected',
|
|
65
|
+
'unstable', 'broken',
|
|
66
|
+
]);
|
|
67
|
+
const successWords = new Set([
|
|
68
|
+
'done', 'success', 'succeeded', 'complete', 'completed', 'finished',
|
|
69
|
+
'passed', 'ok', 'green', 'healthy',
|
|
70
|
+
]);
|
|
71
|
+
for (const f of fields) {
|
|
72
|
+
const raw = obj[f];
|
|
73
|
+
if (raw == null) continue;
|
|
74
|
+
const v = String(raw).toLowerCase().trim();
|
|
75
|
+
if (!v) continue;
|
|
76
|
+
if (failureWords.has(v)) return { failure: true, source: f, value: v };
|
|
77
|
+
if (successWords.has(v)) return { failure: false, source: f, value: v };
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
43
82
|
const g = globalThis as any;
|
|
44
83
|
|
|
45
84
|
export function startWatchRunner(hooks: WatchRunnerHooks = {}): void {
|
|
@@ -120,7 +159,27 @@ export function startWatchRunner(hooks: WatchRunnerHooks = {}): void {
|
|
|
120
159
|
if (w.fail_path && truthy(getPath(obj, w.fail_path))) {
|
|
121
160
|
return finish(w, 'failed', obj, `${w.label}: failure condition met.`);
|
|
122
161
|
}
|
|
123
|
-
//
|
|
162
|
+
// Hard terminal check — if the poll tool itself says "this is a
|
|
163
|
+
// terminal state" (cancelled / failed / done / etc.), believe it
|
|
164
|
+
// regardless of the user-configured done condition. Without this,
|
|
165
|
+
// a watch on get_pipeline_status with done_match={status:"done"}
|
|
166
|
+
// would keep polling after the user cancels the pipeline, because
|
|
167
|
+
// status="cancelled" never matches "done" — wasting polls until
|
|
168
|
+
// max_polls / timeout. The builtin status tools (get_pipeline_status,
|
|
169
|
+
// get_task_status) all set obj.terminal = true on cancelled/failed
|
|
170
|
+
// too, so honoring it here drops the watch the moment the user
|
|
171
|
+
// intervenes.
|
|
172
|
+
if (truthy(getPath(obj, 'terminal'))) {
|
|
173
|
+
const statusVal = String(getPath(obj, 'status') || '').toLowerCase();
|
|
174
|
+
const isFailureLike = statusVal === 'failed' || statusVal === 'cancelled';
|
|
175
|
+
return finish(
|
|
176
|
+
w,
|
|
177
|
+
isFailureLike ? 'failed' : 'done',
|
|
178
|
+
obj,
|
|
179
|
+
`${w.label}: ${statusVal || 'reached a terminal state'}.`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
// done check (user-configured)
|
|
124
183
|
let done = false;
|
|
125
184
|
if (w.done_match) {
|
|
126
185
|
const v = getPath(obj, w.done_match.path);
|
|
@@ -132,6 +191,22 @@ export function startWatchRunner(hooks: WatchRunnerHooks = {}): void {
|
|
|
132
191
|
if (done) {
|
|
133
192
|
return finish(w, 'done', obj, `${w.label}: done.`);
|
|
134
193
|
}
|
|
194
|
+
// Heuristic terminal detection — fallback for connector pollers
|
|
195
|
+
// that don't set obj.terminal AND whose authors didn't anticipate
|
|
196
|
+
// a particular done condition. If the poll result has a common
|
|
197
|
+
// "I'm finished" shape (state/status/phase/result with a known
|
|
198
|
+
// terminal word, or done:true / finished:true), trust it. User's
|
|
199
|
+
// explicit done_match/done_path runs first (above), so a watch
|
|
200
|
+
// wanting "done when status==running" still works as intended.
|
|
201
|
+
const term = detectTerminalState(obj);
|
|
202
|
+
if (term) {
|
|
203
|
+
return finish(
|
|
204
|
+
w,
|
|
205
|
+
term.failure ? 'failed' : 'done',
|
|
206
|
+
obj,
|
|
207
|
+
`${w.label}: detected ${term.source}=${term.value} — closing watch.`,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
135
210
|
// not done — bound by polls / timeout, else reschedule
|
|
136
211
|
if (polls >= w.max_polls || now - w.created_at > w.timeout_sec * 1000) {
|
|
137
212
|
return finish(w, 'timed_out', obj, `${w.label}: not done within ${w.max_polls} polls / ${w.timeout_sec}s — please verify manually.`);
|
package/package.json
CHANGED