claw-doing 0.1.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/LICENSE +21 -0
- package/README.md +270 -0
- package/SKILL.md +182 -0
- package/bootstrap/TASK_TRACKER.md +78 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/src/store/task-store.d.ts +11 -0
- package/dist/src/store/task-store.d.ts.map +1 -0
- package/dist/src/store/task-store.js +81 -0
- package/dist/src/store/task-store.js.map +1 -0
- package/dist/src/tools/task-complete.d.ts +19 -0
- package/dist/src/tools/task-complete.d.ts.map +1 -0
- package/dist/src/tools/task-complete.js +49 -0
- package/dist/src/tools/task-complete.js.map +1 -0
- package/dist/src/tools/task-plan.d.ts +23 -0
- package/dist/src/tools/task-plan.d.ts.map +1 -0
- package/dist/src/tools/task-plan.js +36 -0
- package/dist/src/tools/task-plan.js.map +1 -0
- package/dist/src/tools/task-step-done.d.ts +19 -0
- package/dist/src/tools/task-step-done.d.ts.map +1 -0
- package/dist/src/tools/task-step-done.js +45 -0
- package/dist/src/tools/task-step-done.js.map +1 -0
- package/dist/src/types.d.ts +28 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/format.d.ts +5 -0
- package/dist/src/utils/format.d.ts.map +1 -0
- package/dist/src/utils/format.js +39 -0
- package/dist/src/utils/format.js.map +1 -0
- package/openclaw.plugin.json +21 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 seekcontext
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# claw-doing
|
|
2
|
+
|
|
3
|
+
> See what your agent is doing — right now.
|
|
4
|
+
|
|
5
|
+
**claw-doing** is an [OpenClaw](https://openclaws.io) plugin that gives long-running agent tasks real-time progress visibility. Instead of waiting silently for minutes while your agent works, you get a live step-by-step progress feed — right in your messaging channel.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## The problem
|
|
10
|
+
|
|
11
|
+
When you ask OpenClaw to do something complex — research a topic, process files, build a multi-step pipeline — the interaction goes silent:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
You: "Research AI agent frameworks and write a report"
|
|
15
|
+
Agent: [silent for 8 minutes]
|
|
16
|
+
You: "...is it still running?"
|
|
17
|
+
Agent: [finally replies, or times out]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
You have no idea what's happening, whether it's stuck, or when it will finish.
|
|
21
|
+
|
|
22
|
+
## What claw-doing does
|
|
23
|
+
|
|
24
|
+
With claw-doing installed, the same interaction looks like this:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
You: "Research AI agent frameworks and write a report"
|
|
28
|
+
|
|
29
|
+
Agent: Research AI agent frameworks
|
|
30
|
+
Task ID: `a3f8c2d1-...`
|
|
31
|
+
|
|
32
|
+
Plan — 4 steps:
|
|
33
|
+
1. Search and collect sources (~3 min)
|
|
34
|
+
2. Extract key findings (~2 min)
|
|
35
|
+
3. Cross-reference and validate (~2 min)
|
|
36
|
+
4. Write and deliver report (~3 min)
|
|
37
|
+
|
|
38
|
+
[░░░░░░░░░░] 0/4
|
|
39
|
+
Starting now...
|
|
40
|
+
|
|
41
|
+
──── (3 minutes later) ────
|
|
42
|
+
|
|
43
|
+
[███░░░░░░░] 1/4 — elapsed: 3m 12s
|
|
44
|
+
|
|
45
|
+
✓ Step 1: Found 47 sources, filtered to 14 high-quality references
|
|
46
|
+
⚠ Note: Two sources were paywalled — used abstracts only
|
|
47
|
+
|
|
48
|
+
Next: Step 2 — Extract key findings
|
|
49
|
+
|
|
50
|
+
──── (2 minutes later) ────
|
|
51
|
+
|
|
52
|
+
[██████░░░░] 2/4 — elapsed: 5m 8s
|
|
53
|
+
|
|
54
|
+
✓ Step 2: Extracted 31 key data points across 8 frameworks
|
|
55
|
+
|
|
56
|
+
... and so on ...
|
|
57
|
+
|
|
58
|
+
[██████████] 4/4 — 11m 42s total
|
|
59
|
+
|
|
60
|
+
✅ Completed successfully
|
|
61
|
+
|
|
62
|
+
Compiled a 2,800-word report covering 8 frameworks with 12 citations.
|
|
63
|
+
Key finding: LangGraph leads on multi-agent orchestration; OpenClaw leads on channel integrations.
|
|
64
|
+
|
|
65
|
+
Deliverables:
|
|
66
|
+
• memory/2026-03-29-ai-agent-frameworks.md
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
openclaw plugins install claw-doing
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
OpenClaw checks ClawHub first, then falls back to npm automatically.
|
|
78
|
+
|
|
79
|
+
After installation, restart your OpenClaw gateway to load the plugin.
|
|
80
|
+
|
|
81
|
+
### Enable the bootstrap instructions (recommended)
|
|
82
|
+
|
|
83
|
+
The plugin ships with `bootstrap/TASK_TRACKER.md` — a set of instructions that teaches your agent when and how to use the tracking tools. Add it to your OpenClaw workspace bootstrap:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"hooks": {
|
|
88
|
+
"internal": {
|
|
89
|
+
"enabled": true,
|
|
90
|
+
"entries": {
|
|
91
|
+
"bootstrap-extra-files": {
|
|
92
|
+
"enabled": true,
|
|
93
|
+
"paths": ["node_modules/claw-doing/bootstrap/TASK_TRACKER.md"]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Or copy it manually to your workspace:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
cp node_modules/claw-doing/bootstrap/TASK_TRACKER.md ~/.openclaw/workspace/TASK_TRACKER.md
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Then add `TASK_TRACKER.md` to your bootstrap file list so the agent loads it on start.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## How it works
|
|
112
|
+
|
|
113
|
+
claw-doing registers three agent tools. The agent calls them as it works — no separate dashboard or server required.
|
|
114
|
+
|
|
115
|
+
### Tools
|
|
116
|
+
|
|
117
|
+
#### `task_plan`
|
|
118
|
+
|
|
119
|
+
Called at the **start** of a multi-step task. Registers the plan and returns a task ID.
|
|
120
|
+
|
|
121
|
+
| Parameter | Type | Description |
|
|
122
|
+
|-----------|------|-------------|
|
|
123
|
+
| `title` | `string` | Short title for the task |
|
|
124
|
+
| `steps` | `Step[]` | Ordered list of steps |
|
|
125
|
+
| `steps[].index` | `number` | Step number (1-based) |
|
|
126
|
+
| `steps[].description` | `string` | What this step does |
|
|
127
|
+
| `steps[].estimatedMinutes` | `number?` | Optional time estimate |
|
|
128
|
+
|
|
129
|
+
Returns: task ID + formatted plan for the user.
|
|
130
|
+
|
|
131
|
+
#### `task_step_done`
|
|
132
|
+
|
|
133
|
+
Called after **each step** completes. Updates progress and notifies the user.
|
|
134
|
+
|
|
135
|
+
| Parameter | Type | Description |
|
|
136
|
+
|-----------|------|-------------|
|
|
137
|
+
| `taskId` | `string` | Task ID from `task_plan` |
|
|
138
|
+
| `stepIndex` | `number` | Which step was just completed |
|
|
139
|
+
| `summary` | `string` | What was accomplished |
|
|
140
|
+
| `deviation` | `string?` | Unexpected findings (optional) |
|
|
141
|
+
|
|
142
|
+
Returns: progress bar + step summary.
|
|
143
|
+
|
|
144
|
+
#### `task_complete`
|
|
145
|
+
|
|
146
|
+
Called when the **entire task** finishes (or fails). Delivers the final report.
|
|
147
|
+
|
|
148
|
+
| Parameter | Type | Description |
|
|
149
|
+
|-----------|------|-------------|
|
|
150
|
+
| `taskId` | `string` | Task ID from `task_plan` |
|
|
151
|
+
| `outcome` | `"success" \| "partial" \| "failed"` | Task outcome |
|
|
152
|
+
| `summary` | `string` | User-facing completion summary |
|
|
153
|
+
| `artifacts` | `string[]?` | Output files or results produced |
|
|
154
|
+
|
|
155
|
+
Returns: final progress bar + summary + deliverables list.
|
|
156
|
+
|
|
157
|
+
### Task state
|
|
158
|
+
|
|
159
|
+
Each task is persisted as a JSON file at:
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
~/.openclaw/workspace/claw-doing/tasks/<taskId>.json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
This means task state survives agent restarts and can be inspected or archived.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Configuration
|
|
170
|
+
|
|
171
|
+
Optional config in your `openclaw.config.json`:
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"plugins": {
|
|
176
|
+
"claw-doing": {
|
|
177
|
+
"storageDir": "/custom/path/to/tasks",
|
|
178
|
+
"progressBarWidth": 12
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
| Option | Default | Description |
|
|
185
|
+
|--------|---------|-------------|
|
|
186
|
+
| `storageDir` | `~/.openclaw/workspace/claw-doing/tasks/` | Where task JSON files are stored |
|
|
187
|
+
| `progressBarWidth` | `10` | Width of the `█░` progress bar |
|
|
188
|
+
|
|
189
|
+
You can also set the storage directory via environment variable:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
CLAW_DOING_STORAGE_DIR=/my/custom/path openclaw start
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Design principles
|
|
198
|
+
|
|
199
|
+
**Zero deployment.** Unlike dashboard-based solutions, claw-doing requires no external server, database, or Docker container. Install the plugin and it works.
|
|
200
|
+
|
|
201
|
+
**In-channel delivery.** Progress updates arrive in the same messaging channel you're already using — Discord, Telegram, WhatsApp, web chat. You don't need to open a separate UI.
|
|
202
|
+
|
|
203
|
+
**Agent-driven, not hook-driven.** The agent explicitly calls `task_step_done` after each step. This makes progress reporting intentional and semantically rich (the agent decides what counts as "done" and what to say about it), rather than relying on low-level hook instrumentation that may fire at the wrong granularity.
|
|
204
|
+
|
|
205
|
+
**Persistent state.** Task records are written to disk as JSON. They can be read, archived, or used by other tooling (dashboards, scripts) independently.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Comparison with similar projects
|
|
210
|
+
|
|
211
|
+
| | claw-doing | ClawBeacon |
|
|
212
|
+
|---|---|---|
|
|
213
|
+
| **Form factor** | OpenClaw plugin (zero deployment) | Separate web app (Docker/Railway) |
|
|
214
|
+
| **Where you see progress** | Your chat channel | A Kanban dashboard in a browser |
|
|
215
|
+
| **Setup** | `openclaw plugins install claw-doing` | Clone, deploy backend + frontend |
|
|
216
|
+
| **Best for** | Individual users, any task | Teams managing multiple agents |
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Development
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
git clone https://github.com/seekcontext/claw-doing
|
|
224
|
+
cd claw-doing
|
|
225
|
+
npm install
|
|
226
|
+
npm run dev # watch mode
|
|
227
|
+
npm run typecheck # type check without emitting
|
|
228
|
+
npm run build # compile to dist/
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Project structure
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
claw-doing/
|
|
235
|
+
├── index.ts # Plugin entry — registers all tools
|
|
236
|
+
├── src/
|
|
237
|
+
│ ├── types.ts # Shared Task and TaskStep interfaces
|
|
238
|
+
│ ├── store/
|
|
239
|
+
│ │ └── task-store.ts # File-based task state (create, update, complete)
|
|
240
|
+
│ ├── tools/
|
|
241
|
+
│ │ ├── task-plan.ts # task_plan tool handler
|
|
242
|
+
│ │ ├── task-step-done.ts # task_step_done tool handler
|
|
243
|
+
│ │ └── task-complete.ts # task_complete tool handler
|
|
244
|
+
│ └── utils/
|
|
245
|
+
│ └── format.ts # Progress bar, elapsed time, outcome labels
|
|
246
|
+
├── bootstrap/
|
|
247
|
+
│ └── TASK_TRACKER.md # Agent behavior instructions
|
|
248
|
+
├── openclaw.plugin.json # Plugin manifest
|
|
249
|
+
└── openclaw.d.ts # Type stubs for openclaw peer dependency
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Roadmap
|
|
255
|
+
|
|
256
|
+
- [ ] `before_tool_call` hook to auto-detect `sessions_spawn` and link sub-agents to parent tasks
|
|
257
|
+
- [ ] Deviation detection: flag steps where output diverges significantly from expected
|
|
258
|
+
- [ ] `task_stuck` tool: pause execution and ask the user for guidance mid-task
|
|
259
|
+
- [ ] Task history CLI: `openclaw doing list` to review past task records
|
|
260
|
+
- [ ] Integration with `OpenClawTaskStatusV1` (openclaw/openclaw#42385) when merged
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Contributing
|
|
265
|
+
|
|
266
|
+
Pull requests welcome. Please open an issue first for significant changes. Repository: [github.com/seekcontext/claw-doing](https://github.com/seekcontext/claw-doing)
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
MIT
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claw-doing
|
|
3
|
+
description: "Enables real-time progress tracking for long-running tasks in OpenClaw. Use when starting any complex task with 3+ steps, when the user asks for a progress update during long work, or when you are about to do multi-step research, file generation, or sub-agent coordination. Requires the claw-doing plugin: openclaw plugins install claw-doing"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claw Doing — Task Progress Tracking
|
|
7
|
+
|
|
8
|
+
This skill teaches you to use the **claw-doing** plugin to keep users informed during any long or complex task. Instead of going silent while you work, you report progress after every step — directly in the user's chat channel.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
The `claw-doing` plugin must be installed on the user's OpenClaw gateway:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
openclaw plugins install claw-doing
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
After installation, restart the gateway. The plugin registers three tools: `task_plan`, `task_step_done`, and `task_complete`.
|
|
19
|
+
|
|
20
|
+
## When to use this skill
|
|
21
|
+
|
|
22
|
+
Use progress tracking whenever the task you are about to perform:
|
|
23
|
+
|
|
24
|
+
- Has **3 or more distinct steps**
|
|
25
|
+
- Will take **more than ~2 minutes**
|
|
26
|
+
- Involves web research, file generation, data processing, or spawning sub-agents
|
|
27
|
+
- Is the kind of task where the user might wonder "is it still running?"
|
|
28
|
+
|
|
29
|
+
If in doubt, use it. It costs nothing and significantly improves the user experience.
|
|
30
|
+
|
|
31
|
+
## Workflow
|
|
32
|
+
|
|
33
|
+
### Step 1 — Declare the plan (before you do anything else)
|
|
34
|
+
|
|
35
|
+
The very first thing you do, before executing any step, is call `task_plan`. This registers the task and shows the user what to expect.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
task_plan({
|
|
39
|
+
title: "Research AI agent frameworks",
|
|
40
|
+
steps: [
|
|
41
|
+
{ index: 1, description: "Search and collect sources", estimatedMinutes: 3 },
|
|
42
|
+
{ index: 2, description: "Extract key findings", estimatedMinutes: 2 },
|
|
43
|
+
{ index: 3, description: "Cross-reference and validate", estimatedMinutes: 2 },
|
|
44
|
+
{ index: 4, description: "Write and deliver report", estimatedMinutes: 3 }
|
|
45
|
+
]
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The tool returns a formatted plan and a **task ID**. Send the tool output to the user immediately — do not paraphrase or delay it.
|
|
50
|
+
|
|
51
|
+
Save the task ID: you need it for every subsequent call.
|
|
52
|
+
|
|
53
|
+
### Step 2 — Report after each step
|
|
54
|
+
|
|
55
|
+
Immediately after completing a step (before starting the next one), call `task_step_done`:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
task_step_done({
|
|
59
|
+
taskId: "<taskId from task_plan>",
|
|
60
|
+
stepIndex: 1,
|
|
61
|
+
summary: "Found 47 sources, filtered to 14 high-quality references",
|
|
62
|
+
deviation: "Two sources were paywalled — used abstracts only"
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The `deviation` field is optional. Use it when something unexpected happened — a source was unavailable, a result was ambiguous, you made a judgment call. This keeps the user informed about decisions you made on their behalf.
|
|
67
|
+
|
|
68
|
+
Send the tool output to the user immediately.
|
|
69
|
+
|
|
70
|
+
### Step 3 — Close the task when finished
|
|
71
|
+
|
|
72
|
+
When the entire task is complete — or if it has definitively failed — call `task_complete`:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
task_complete({
|
|
76
|
+
taskId: "<taskId>",
|
|
77
|
+
outcome: "success",
|
|
78
|
+
summary: "Compiled a 2,800-word report covering 8 frameworks with 12 citations. Key finding: LangGraph leads on orchestration; OpenClaw leads on channel integrations.",
|
|
79
|
+
artifacts: ["memory/2026-03-29-ai-agent-frameworks.md"]
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Always call `task_complete`, even on failure — it closes the task record cleanly.
|
|
84
|
+
|
|
85
|
+
Send the tool output to the user. This is the user's final delivery notification.
|
|
86
|
+
|
|
87
|
+
## Outcome values
|
|
88
|
+
|
|
89
|
+
| Value | When to use |
|
|
90
|
+
|-------|-------------|
|
|
91
|
+
| `success` | All steps completed, output matches the user's intent |
|
|
92
|
+
| `partial` | Some steps had issues, but you produced useful output |
|
|
93
|
+
| `failed` | The task could not be completed |
|
|
94
|
+
|
|
95
|
+
## Rules
|
|
96
|
+
|
|
97
|
+
- Call `task_plan` **before** executing anything — never retroactively.
|
|
98
|
+
- Call `task_complete` **always** — even when the task fails.
|
|
99
|
+
- Relay tool output to the user **immediately** after each call.
|
|
100
|
+
- Keep `summary` fields **factual and brief** — 1 to 2 sentences.
|
|
101
|
+
- Use `deviation` **honestly** — it builds user trust.
|
|
102
|
+
- Never skip `task_step_done` for a completed step, even if you are in a hurry.
|
|
103
|
+
|
|
104
|
+
## Full example
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
User: "Scrape the top 20 Hacker News posts this week and summarize them"
|
|
108
|
+
|
|
109
|
+
You call:
|
|
110
|
+
task_plan({
|
|
111
|
+
title: "Summarize top 20 HN posts",
|
|
112
|
+
steps: [
|
|
113
|
+
{ index: 1, description: "Fetch HN front page and extract post URLs", estimatedMinutes: 1 },
|
|
114
|
+
{ index: 2, description: "Scrape content from each post", estimatedMinutes: 4 },
|
|
115
|
+
{ index: 3, description: "Generate per-post summaries", estimatedMinutes: 3 },
|
|
116
|
+
{ index: 4, description: "Compile and deliver digest", estimatedMinutes: 2 }
|
|
117
|
+
]
|
|
118
|
+
})
|
|
119
|
+
→ relay output to user
|
|
120
|
+
|
|
121
|
+
(execute step 1)
|
|
122
|
+
|
|
123
|
+
task_step_done({ taskId, stepIndex: 1, summary: "Fetched 20 post URLs from HN front page" })
|
|
124
|
+
→ relay output to user
|
|
125
|
+
|
|
126
|
+
(execute step 2)
|
|
127
|
+
|
|
128
|
+
task_step_done({
|
|
129
|
+
taskId,
|
|
130
|
+
stepIndex: 2,
|
|
131
|
+
summary: "Scraped 18 of 20 posts",
|
|
132
|
+
deviation: "2 posts linked to PDFs — skipped, will note in digest"
|
|
133
|
+
})
|
|
134
|
+
→ relay output to user
|
|
135
|
+
|
|
136
|
+
(execute steps 3–4 similarly)
|
|
137
|
+
|
|
138
|
+
task_complete({
|
|
139
|
+
taskId,
|
|
140
|
+
outcome: "partial",
|
|
141
|
+
summary: "Digest covers 18 of 20 posts. Two PDF links were skipped and noted.",
|
|
142
|
+
artifacts: ["memory/2026-03-29-hn-digest.md"]
|
|
143
|
+
})
|
|
144
|
+
→ relay output to user
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## What the user sees
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
[░░░░░░░░░░] 0/4 — Starting now...
|
|
151
|
+
|
|
152
|
+
[███░░░░░░░] 1/4 — elapsed: 1m 2s
|
|
153
|
+
✓ Step 1: Fetched 20 post URLs from HN front page
|
|
154
|
+
|
|
155
|
+
[██████░░░░] 2/4 — elapsed: 5m 14s
|
|
156
|
+
✓ Step 2: Scraped 18 of 20 posts
|
|
157
|
+
⚠ Note: 2 posts linked to PDFs — skipped, will note in digest
|
|
158
|
+
|
|
159
|
+
...
|
|
160
|
+
|
|
161
|
+
[██████████] 4/4 — 10m 33s total
|
|
162
|
+
⚠️ Partially completed
|
|
163
|
+
|
|
164
|
+
Digest covers 18 of 20 posts. Two PDF links were skipped and noted.
|
|
165
|
+
|
|
166
|
+
Deliverables:
|
|
167
|
+
• memory/2026-03-29-hn-digest.md
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## FAQ
|
|
171
|
+
|
|
172
|
+
**What if the task only has 2 steps?**
|
|
173
|
+
Skip the tracking tools — they add overhead without much benefit for very short tasks. Use your judgment: if the user will wait more than 2 minutes, use tracking.
|
|
174
|
+
|
|
175
|
+
**What if a step fails mid-task?**
|
|
176
|
+
Call `task_step_done` with `deviation` explaining what went wrong, then decide whether to continue, adapt, or call `task_complete` with `outcome: "failed"`.
|
|
177
|
+
|
|
178
|
+
**What if I don't know the number of steps upfront?**
|
|
179
|
+
Estimate. A rough plan with approximate steps is much better than no plan. You can add a note in `summary` at completion if the actual steps differed.
|
|
180
|
+
|
|
181
|
+
**Where is the task data stored?**
|
|
182
|
+
In `~/.openclaw/workspace/claw-doing/tasks/<taskId>.json`. Each task is a JSON file the user can inspect or archive.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Task Tracker Protocol
|
|
2
|
+
|
|
3
|
+
You have access to real-time progress tracking tools from the **claw-doing** plugin.
|
|
4
|
+
Use them to keep the user informed during any long or complex task.
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
|
|
8
|
+
Use the task tracking tools whenever you are about to perform a task that:
|
|
9
|
+
|
|
10
|
+
- Has **3 or more distinct steps**
|
|
11
|
+
- Will take **more than ~2 minutes** to complete
|
|
12
|
+
- Involves sub-agent spawning, file generation, web research, or multi-stage processing
|
|
13
|
+
|
|
14
|
+
## Required workflow
|
|
15
|
+
|
|
16
|
+
### 1. Declare the plan at the start
|
|
17
|
+
|
|
18
|
+
Before executing anything, call `task_plan`:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
task_plan({
|
|
22
|
+
title: "Research AI agent frameworks",
|
|
23
|
+
steps: [
|
|
24
|
+
{ index: 1, description: "Search and collect sources", estimatedMinutes: 3 },
|
|
25
|
+
{ index: 2, description: "Extract key findings", estimatedMinutes: 2 },
|
|
26
|
+
{ index: 3, description: "Cross-reference and validate", estimatedMinutes: 2 },
|
|
27
|
+
{ index: 4, description: "Write and deliver report", estimatedMinutes: 3 }
|
|
28
|
+
]
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Immediately relay the tool output to the user** as your message. Do not add extra preamble.
|
|
33
|
+
|
|
34
|
+
### 2. Report after each step
|
|
35
|
+
|
|
36
|
+
After you finish each step, call `task_step_done` before starting the next:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
task_step_done({
|
|
40
|
+
taskId: "<taskId from task_plan>",
|
|
41
|
+
stepIndex: 1,
|
|
42
|
+
summary: "Found 47 sources, filtered to 14 high-quality references",
|
|
43
|
+
deviation: "Two sources were paywalled — used abstracts only" // optional
|
|
44
|
+
})
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Relay the output to the user immediately.** The deviation field is optional — only include it if something unexpected happened.
|
|
48
|
+
|
|
49
|
+
### 3. Close the task when finished
|
|
50
|
+
|
|
51
|
+
When the task is fully complete (or has definitively failed), call `task_complete`:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
task_complete({
|
|
55
|
+
taskId: "<taskId>",
|
|
56
|
+
outcome: "success", // or "partial" or "failed"
|
|
57
|
+
summary: "Compiled a 2,800-word report covering 8 frameworks with 12 citations.",
|
|
58
|
+
artifacts: ["memory/2026-03-29-ai-agent-report.md"]
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Relay the output to the user.** This closes the task record.
|
|
63
|
+
|
|
64
|
+
## Outcome values
|
|
65
|
+
|
|
66
|
+
| Outcome | When to use |
|
|
67
|
+
|---------|-------------|
|
|
68
|
+
| `success` | All steps completed, output matches intent |
|
|
69
|
+
| `partial` | Some steps had issues but useful output was produced |
|
|
70
|
+
| `failed` | Task could not be completed — still call this to close the record |
|
|
71
|
+
|
|
72
|
+
## Rules
|
|
73
|
+
|
|
74
|
+
- **Always** call `task_plan` before starting a qualifying task — not after.
|
|
75
|
+
- **Always** call `task_complete` at the end, even on failure.
|
|
76
|
+
- Relay tool output directly to the user; do not paraphrase or delay it.
|
|
77
|
+
- Use the `deviation` field honestly — it helps the user understand what changed.
|
|
78
|
+
- Keep step summaries factual and brief (1–2 sentences max).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";AAaA,wBA2CG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
|
+
import { TaskPlanSchema, executeTaskPlan } from "./src/tools/task-plan.js";
|
|
3
|
+
import { TaskStepDoneSchema, executeTaskStepDone, } from "./src/tools/task-step-done.js";
|
|
4
|
+
import { TaskCompleteSchema, executeTaskComplete, } from "./src/tools/task-complete.js";
|
|
5
|
+
export default definePluginEntry({
|
|
6
|
+
id: "claw-doing",
|
|
7
|
+
name: "Claw Doing",
|
|
8
|
+
description: "Real-time progress tracking for long-running agent tasks. See what your agent is doing — right now.",
|
|
9
|
+
register(api) {
|
|
10
|
+
api.registerTool({
|
|
11
|
+
name: "task_plan",
|
|
12
|
+
description: "Call this at the START of any complex task that has 3 or more distinct steps. " +
|
|
13
|
+
"Declares the task plan to the user and returns a task ID needed for subsequent progress calls. " +
|
|
14
|
+
"Always call task_step_done after completing each step, and task_complete when finished.",
|
|
15
|
+
parameters: TaskPlanSchema,
|
|
16
|
+
async execute(_id, params) {
|
|
17
|
+
return executeTaskPlan(_id, params);
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
api.registerTool({
|
|
21
|
+
name: "task_step_done",
|
|
22
|
+
description: "Call this immediately after completing each step of a task registered with task_plan. " +
|
|
23
|
+
"Reports progress to the user with a visual progress bar and step summary. " +
|
|
24
|
+
"Include a deviation note if the step produced unexpected results.",
|
|
25
|
+
parameters: TaskStepDoneSchema,
|
|
26
|
+
async execute(_id, params) {
|
|
27
|
+
return executeTaskStepDone(_id, params);
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
api.registerTool({
|
|
31
|
+
name: "task_complete",
|
|
32
|
+
description: "Call this when a task registered with task_plan has finished (success, partial, or failed). " +
|
|
33
|
+
"Delivers the final summary and list of produced artifacts to the user. " +
|
|
34
|
+
"Always call this even if the task failed — it closes the task record.",
|
|
35
|
+
parameters: TaskCompleteSchema,
|
|
36
|
+
async execute(_id, params) {
|
|
37
|
+
return executeTaskComplete(_id, params);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAuB,MAAM,0BAA0B,CAAC;AAChG,OAAO,EACL,kBAAkB,EAClB,mBAAmB,GAEpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,kBAAkB,EAClB,mBAAmB,GAEpB,MAAM,8BAA8B,CAAC;AAEtC,eAAe,iBAAiB,CAAC;IAC/B,EAAE,EAAE,YAAY;IAChB,IAAI,EAAE,YAAY;IAClB,WAAW,EACT,qGAAqG;IAEvG,QAAQ,CAAC,GAAG;QACV,GAAG,CAAC,YAAY,CAAC;YACf,IAAI,EAAE,WAAW;YACjB,WAAW,EACT,gFAAgF;gBAChF,iGAAiG;gBACjG,yFAAyF;YAC3F,UAAU,EAAE,cAAc;YAC1B,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM;gBACvB,OAAO,eAAe,CAAC,GAAG,EAAE,MAAwB,CAAC,CAAC;YACxD,CAAC;SACF,CAAC,CAAC;QAEH,GAAG,CAAC,YAAY,CAAC;YACf,IAAI,EAAE,gBAAgB;YACtB,WAAW,EACT,wFAAwF;gBACxF,4EAA4E;gBAC5E,mEAAmE;YACrE,UAAU,EAAE,kBAAkB;YAC9B,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM;gBACvB,OAAO,mBAAmB,CAAC,GAAG,EAAE,MAA4B,CAAC,CAAC;YAChE,CAAC;SACF,CAAC,CAAC;QAEH,GAAG,CAAC,YAAY,CAAC;YACf,IAAI,EAAE,eAAe;YACrB,WAAW,EACT,8FAA8F;gBAC9F,yEAAyE;gBACzE,uEAAuE;YACzE,UAAU,EAAE,kBAAkB;YAC9B,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM;gBACvB,OAAO,mBAAmB,CAAC,GAAG,EAAE,MAA4B,CAAC,CAAC;YAChE,CAAC;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Task } from "../types.js";
|
|
2
|
+
export declare function saveTask(task: Task): Promise<void>;
|
|
3
|
+
export declare function loadTask(taskId: string): Promise<Task | null>;
|
|
4
|
+
export declare function createTask(title: string, rawSteps: Array<{
|
|
5
|
+
index: number;
|
|
6
|
+
description: string;
|
|
7
|
+
estimatedMinutes?: number;
|
|
8
|
+
}>): Task;
|
|
9
|
+
export declare function markStepDone(taskId: string, stepIndex: number, summary: string, deviation?: string): Promise<Task | null>;
|
|
10
|
+
export declare function completeTask(taskId: string, outcome: "success" | "partial" | "failed", summary: string, artifacts?: string[]): Promise<Task | null>;
|
|
11
|
+
//# sourceMappingURL=task-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../../src/store/task-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,aAAa,CAAC;AAiBlD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAIxD;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAOnE;AAED,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACjF,IAAI,CAmBN;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAiBtB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,EACzC,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAmBtB"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
function getStorageDir() {
|
|
5
|
+
return (process.env["CLAW_DOING_STORAGE_DIR"] ??
|
|
6
|
+
join(homedir(), ".openclaw", "workspace", "claw-doing", "tasks"));
|
|
7
|
+
}
|
|
8
|
+
async function ensureDir(dir) {
|
|
9
|
+
await fs.mkdir(dir, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
function taskFilePath(taskId) {
|
|
12
|
+
return join(getStorageDir(), `${taskId}.json`);
|
|
13
|
+
}
|
|
14
|
+
export async function saveTask(task) {
|
|
15
|
+
const dir = getStorageDir();
|
|
16
|
+
await ensureDir(dir);
|
|
17
|
+
await fs.writeFile(taskFilePath(task.taskId), JSON.stringify(task, null, 2), "utf-8");
|
|
18
|
+
}
|
|
19
|
+
export async function loadTask(taskId) {
|
|
20
|
+
try {
|
|
21
|
+
const raw = await fs.readFile(taskFilePath(taskId), "utf-8");
|
|
22
|
+
return JSON.parse(raw);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function createTask(title, rawSteps) {
|
|
29
|
+
const now = new Date().toISOString();
|
|
30
|
+
const steps = rawSteps.map((s) => ({
|
|
31
|
+
index: s.index,
|
|
32
|
+
description: s.description,
|
|
33
|
+
estimatedMinutes: s.estimatedMinutes,
|
|
34
|
+
status: "pending",
|
|
35
|
+
}));
|
|
36
|
+
return {
|
|
37
|
+
taskId: crypto.randomUUID(),
|
|
38
|
+
title,
|
|
39
|
+
steps,
|
|
40
|
+
totalSteps: steps.length,
|
|
41
|
+
completedSteps: 0,
|
|
42
|
+
status: "running",
|
|
43
|
+
createdAt: now,
|
|
44
|
+
updatedAt: now,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export async function markStepDone(taskId, stepIndex, summary, deviation) {
|
|
48
|
+
const task = await loadTask(taskId);
|
|
49
|
+
if (!task)
|
|
50
|
+
return null;
|
|
51
|
+
const step = task.steps.find((s) => s.index === stepIndex);
|
|
52
|
+
if (!step)
|
|
53
|
+
return null;
|
|
54
|
+
step.status = "done";
|
|
55
|
+
step.summary = summary;
|
|
56
|
+
step.deviation = deviation;
|
|
57
|
+
step.completedAt = new Date().toISOString();
|
|
58
|
+
task.completedSteps = task.steps.filter((s) => s.status === "done").length;
|
|
59
|
+
task.updatedAt = new Date().toISOString();
|
|
60
|
+
await saveTask(task);
|
|
61
|
+
return task;
|
|
62
|
+
}
|
|
63
|
+
export async function completeTask(taskId, outcome, summary, artifacts) {
|
|
64
|
+
const task = await loadTask(taskId);
|
|
65
|
+
if (!task)
|
|
66
|
+
return null;
|
|
67
|
+
const statusMap = {
|
|
68
|
+
success: "done",
|
|
69
|
+
partial: "partial",
|
|
70
|
+
failed: "failed",
|
|
71
|
+
};
|
|
72
|
+
task.status = statusMap[outcome];
|
|
73
|
+
task.outcome = outcome;
|
|
74
|
+
task.finalSummary = summary;
|
|
75
|
+
task.artifacts = artifacts;
|
|
76
|
+
task.completedAt = new Date().toISOString();
|
|
77
|
+
task.updatedAt = new Date().toISOString();
|
|
78
|
+
await saveTask(task);
|
|
79
|
+
return task;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=task-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-store.js","sourceRoot":"","sources":["../../../src/store/task-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const TaskCompleteSchema: import("@sinclair/typebox").TObject<{
|
|
2
|
+
taskId: import("@sinclair/typebox").TString;
|
|
3
|
+
outcome: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"success">, import("@sinclair/typebox").TLiteral<"partial">, import("@sinclair/typebox").TLiteral<"failed">]>;
|
|
4
|
+
summary: import("@sinclair/typebox").TString;
|
|
5
|
+
artifacts: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
6
|
+
}>;
|
|
7
|
+
export type TaskCompleteParams = {
|
|
8
|
+
taskId: string;
|
|
9
|
+
outcome: "success" | "partial" | "failed";
|
|
10
|
+
summary: string;
|
|
11
|
+
artifacts?: string[];
|
|
12
|
+
};
|
|
13
|
+
export declare function executeTaskComplete(_id: string, params: TaskCompleteParams): Promise<{
|
|
14
|
+
content: {
|
|
15
|
+
type: "text";
|
|
16
|
+
text: string;
|
|
17
|
+
}[];
|
|
18
|
+
}>;
|
|
19
|
+
//# sourceMappingURL=task-complete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-complete.d.ts","sourceRoot":"","sources":["../../../src/tools/task-complete.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,kBAAkB;;;;;EAmB7B,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB;;;;;GA2ChF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { completeTask } from "../store/task-store.js";
|
|
3
|
+
import { buildProgressBar, formatElapsed, formatOutcome } from "../utils/format.js";
|
|
4
|
+
export const TaskCompleteSchema = Type.Object({
|
|
5
|
+
taskId: Type.String({ description: "The task ID returned by task_plan" }),
|
|
6
|
+
outcome: Type.Union([Type.Literal("success"), Type.Literal("partial"), Type.Literal("failed")], {
|
|
7
|
+
description: "'success' if all steps completed as planned, 'partial' if some steps had issues but useful output was produced, 'failed' if the task could not be completed",
|
|
8
|
+
}),
|
|
9
|
+
summary: Type.String({
|
|
10
|
+
description: "A clear, user-facing summary of what was accomplished. Include key numbers, findings, or decisions made.",
|
|
11
|
+
}),
|
|
12
|
+
artifacts: Type.Optional(Type.Array(Type.String(), {
|
|
13
|
+
description: "List of output file paths, URLs, or named results produced by this task (e.g. 'memory/2026-03-29-report.md')",
|
|
14
|
+
})),
|
|
15
|
+
});
|
|
16
|
+
export async function executeTaskComplete(_id, params) {
|
|
17
|
+
const task = await completeTask(params.taskId, params.outcome, params.summary, params.artifacts);
|
|
18
|
+
if (!task) {
|
|
19
|
+
return {
|
|
20
|
+
content: [
|
|
21
|
+
{
|
|
22
|
+
type: "text",
|
|
23
|
+
text: `Error: Task \`${params.taskId}\` not found. Please check the task ID.`,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const bar = buildProgressBar(task.completedSteps, task.totalSteps);
|
|
29
|
+
const elapsed = formatElapsed(task.createdAt);
|
|
30
|
+
const outcomeLabel = formatOutcome(params.outcome);
|
|
31
|
+
const lines = [
|
|
32
|
+
`${bar} — ${elapsed} total`,
|
|
33
|
+
``,
|
|
34
|
+
`${outcomeLabel}`,
|
|
35
|
+
``,
|
|
36
|
+
params.summary,
|
|
37
|
+
];
|
|
38
|
+
if (params.artifacts && params.artifacts.length > 0) {
|
|
39
|
+
lines.push(``, `Deliverables:`);
|
|
40
|
+
params.artifacts.forEach((a) => lines.push(` • ${a}`));
|
|
41
|
+
}
|
|
42
|
+
const failedSteps = task.steps.filter((s) => s.status === "failed");
|
|
43
|
+
if (failedSteps.length > 0) {
|
|
44
|
+
lines.push(``, `Incomplete steps:`);
|
|
45
|
+
failedSteps.forEach((s) => lines.push(` • Step ${s.index}: ${s.description}`));
|
|
46
|
+
}
|
|
47
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=task-complete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-complete.js","sourceRoot":"","sources":["../../../src/tools/task-complete.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const TaskPlanSchema: import("@sinclair/typebox").TObject<{
|
|
2
|
+
title: import("@sinclair/typebox").TString;
|
|
3
|
+
steps: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
4
|
+
index: import("@sinclair/typebox").TNumber;
|
|
5
|
+
description: import("@sinclair/typebox").TString;
|
|
6
|
+
estimatedMinutes: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
7
|
+
}>>;
|
|
8
|
+
}>;
|
|
9
|
+
export type TaskPlanParams = {
|
|
10
|
+
title: string;
|
|
11
|
+
steps: Array<{
|
|
12
|
+
index: number;
|
|
13
|
+
description: string;
|
|
14
|
+
estimatedMinutes?: number;
|
|
15
|
+
}>;
|
|
16
|
+
};
|
|
17
|
+
export declare function executeTaskPlan(_id: string, params: TaskPlanParams): Promise<{
|
|
18
|
+
content: {
|
|
19
|
+
type: "text";
|
|
20
|
+
text: string;
|
|
21
|
+
}[];
|
|
22
|
+
}>;
|
|
23
|
+
//# sourceMappingURL=task-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-plan.d.ts","sourceRoot":"","sources":["../../../src/tools/task-plan.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc;;;;;;;EAczB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjF,CAAC;AAEF,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc;;;;;GAyBxE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { createTask, saveTask } from "../store/task-store.js";
|
|
3
|
+
import { buildProgressBar, formatDuration } from "../utils/format.js";
|
|
4
|
+
export const TaskPlanSchema = Type.Object({
|
|
5
|
+
title: Type.String({
|
|
6
|
+
description: "A short title describing the overall task (e.g. 'Research AI agent frameworks')",
|
|
7
|
+
}),
|
|
8
|
+
steps: Type.Array(Type.Object({
|
|
9
|
+
index: Type.Number({ description: "Step number, starting from 1" }),
|
|
10
|
+
description: Type.String({ description: "What this step does" }),
|
|
11
|
+
estimatedMinutes: Type.Optional(Type.Number({ description: "Estimated duration in minutes" })),
|
|
12
|
+
}), { minItems: 1, description: "Ordered list of steps to complete the task" }),
|
|
13
|
+
});
|
|
14
|
+
export async function executeTaskPlan(_id, params) {
|
|
15
|
+
const task = createTask(params.title, params.steps);
|
|
16
|
+
await saveTask(task);
|
|
17
|
+
const stepList = params.steps
|
|
18
|
+
.map((s) => {
|
|
19
|
+
const eta = s.estimatedMinutes ? ` (${formatDuration(s.estimatedMinutes)})` : "";
|
|
20
|
+
return ` ${s.index}. ${s.description}${eta}`;
|
|
21
|
+
})
|
|
22
|
+
.join("\n");
|
|
23
|
+
const bar = buildProgressBar(0, params.steps.length);
|
|
24
|
+
const message = [
|
|
25
|
+
`**${params.title}**`,
|
|
26
|
+
`Task ID: \`${task.taskId}\``,
|
|
27
|
+
``,
|
|
28
|
+
`Plan — ${params.steps.length} step${params.steps.length > 1 ? "s" : ""}:`,
|
|
29
|
+
stepList,
|
|
30
|
+
``,
|
|
31
|
+
`${bar}`,
|
|
32
|
+
`Starting now...`,
|
|
33
|
+
].join("\n");
|
|
34
|
+
return { content: [{ type: "text", text: message }] };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=task-plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-plan.js","sourceRoot":"","sources":["../../../src/tools/task-plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEtE,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC;IACxC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC;QACjB,WAAW,EAAE,iFAAiF;KAC/F,CAAC;IACF,KAAK,EAAE,IAAI,CAAC,KAAK,CACf,IAAI,CAAC,MAAM,CAAC;QACV,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;QACnE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC;QAChE,gBAAgB,EAAE,IAAI,CAAC,QAAQ,CAC7B,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,+BAA+B,EAAE,CAAC,CAC9D;KACF,CAAC,EACF,EAAE,QAAQ,EAAE,CAAC,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAC3E;CACF,CAAC,CAAC;AAOH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW,EAAE,MAAsB;IACvE,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IAErB,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK;SAC1B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,GAAG,GAAG,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,OAAO,KAAK,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,WAAW,GAAG,GAAG,EAAE,CAAC;IAChD,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,GAAG,GAAG,gBAAgB,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAErD,MAAM,OAAO,GAAG;QACd,KAAK,MAAM,CAAC,KAAK,IAAI;QACrB,cAAc,IAAI,CAAC,MAAM,IAAI;QAC7B,EAAE;QACF,UAAU,MAAM,CAAC,KAAK,CAAC,MAAM,QAAQ,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG;QAC1E,QAAQ;QACR,EAAE;QACF,GAAG,GAAG,EAAE;QACR,iBAAiB;KAClB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const TaskStepDoneSchema: import("@sinclair/typebox").TObject<{
|
|
2
|
+
taskId: import("@sinclair/typebox").TString;
|
|
3
|
+
stepIndex: import("@sinclair/typebox").TNumber;
|
|
4
|
+
summary: import("@sinclair/typebox").TString;
|
|
5
|
+
deviation: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
6
|
+
}>;
|
|
7
|
+
export type TaskStepDoneParams = {
|
|
8
|
+
taskId: string;
|
|
9
|
+
stepIndex: number;
|
|
10
|
+
summary: string;
|
|
11
|
+
deviation?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function executeTaskStepDone(_id: string, params: TaskStepDoneParams): Promise<{
|
|
14
|
+
content: {
|
|
15
|
+
type: "text";
|
|
16
|
+
text: string;
|
|
17
|
+
}[];
|
|
18
|
+
}>;
|
|
19
|
+
//# sourceMappingURL=task-step-done.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-step-done.d.ts","sourceRoot":"","sources":["../../../src/tools/task-step-done.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,kBAAkB;;;;;EAY7B,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB;;;;;GAyChF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { markStepDone } from "../store/task-store.js";
|
|
3
|
+
import { buildProgressBar, formatElapsed } from "../utils/format.js";
|
|
4
|
+
export const TaskStepDoneSchema = Type.Object({
|
|
5
|
+
taskId: Type.String({ description: "The task ID returned by task_plan" }),
|
|
6
|
+
stepIndex: Type.Number({ description: "The step number that was just completed" }),
|
|
7
|
+
summary: Type.String({
|
|
8
|
+
description: "A concise description of what was accomplished in this step",
|
|
9
|
+
}),
|
|
10
|
+
deviation: Type.Optional(Type.String({
|
|
11
|
+
description: "Describe any unexpected finding or deviation from the original plan (optional). Leave empty if everything went as expected.",
|
|
12
|
+
})),
|
|
13
|
+
});
|
|
14
|
+
export async function executeTaskStepDone(_id, params) {
|
|
15
|
+
const task = await markStepDone(params.taskId, params.stepIndex, params.summary, params.deviation);
|
|
16
|
+
if (!task) {
|
|
17
|
+
return {
|
|
18
|
+
content: [
|
|
19
|
+
{
|
|
20
|
+
type: "text",
|
|
21
|
+
text: `Error: Task \`${params.taskId}\` not found. Please check the task ID.`,
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const bar = buildProgressBar(task.completedSteps, task.totalSteps);
|
|
27
|
+
const elapsed = formatElapsed(task.createdAt);
|
|
28
|
+
const isLastStep = task.completedSteps === task.totalSteps;
|
|
29
|
+
const lines = [
|
|
30
|
+
`${bar} — elapsed: ${elapsed}`,
|
|
31
|
+
``,
|
|
32
|
+
`✓ Step ${params.stepIndex}: ${params.summary}`,
|
|
33
|
+
];
|
|
34
|
+
if (params.deviation) {
|
|
35
|
+
lines.push(``, `⚠ Note: ${params.deviation}`);
|
|
36
|
+
}
|
|
37
|
+
if (!isLastStep) {
|
|
38
|
+
const nextStep = task.steps.find((s) => s.status === "pending");
|
|
39
|
+
if (nextStep) {
|
|
40
|
+
lines.push(``, `Next: Step ${nextStep.index} — ${nextStep.description}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=task-step-done.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-step-done.js","sourceRoot":"","sources":["../../../src/tools/task-step-done.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAErE,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5C,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,mCAAmC,EAAE,CAAC;IACzE,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,yCAAyC,EAAE,CAAC;IAClF,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC;QACnB,WAAW,EAAE,6DAA6D;KAC3E,CAAC;IACF,SAAS,EAAE,IAAI,CAAC,QAAQ,CACtB,IAAI,CAAC,MAAM,CAAC;QACV,WAAW,EACT,6HAA6H;KAChI,CAAC,CACH;CACF,CAAC,CAAC;AASH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAAW,EAAE,MAA0B;IAC/E,MAAM,IAAI,GAAG,MAAM,YAAY,CAC7B,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,OAAO,EACd,MAAM,CAAC,SAAS,CACjB,CAAC;IAEF,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,iBAAiB,MAAM,CAAC,MAAM,yCAAyC;iBAC9E;aACF;SACF,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACnE,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,KAAK,IAAI,CAAC,UAAU,CAAC;IAE3D,MAAM,KAAK,GAAG;QACZ,GAAG,GAAG,eAAe,OAAO,EAAE;QAC9B,EAAE;QACF,UAAU,MAAM,CAAC,SAAS,KAAK,MAAM,CAAC,OAAO,EAAE;KAChD,CAAC;IAEF,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QAChE,IAAI,QAAQ,EAAE,CAAC;YACb,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,QAAQ,CAAC,KAAK,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;AAC1E,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface TaskStep {
|
|
2
|
+
index: number;
|
|
3
|
+
description: string;
|
|
4
|
+
estimatedMinutes?: number;
|
|
5
|
+
status: "pending" | "done" | "failed";
|
|
6
|
+
summary?: string;
|
|
7
|
+
deviation?: string;
|
|
8
|
+
completedAt?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface Task {
|
|
11
|
+
taskId: string;
|
|
12
|
+
title: string;
|
|
13
|
+
steps: TaskStep[];
|
|
14
|
+
totalSteps: number;
|
|
15
|
+
completedSteps: number;
|
|
16
|
+
status: "running" | "done" | "partial" | "failed";
|
|
17
|
+
createdAt: string;
|
|
18
|
+
updatedAt: string;
|
|
19
|
+
completedAt?: string;
|
|
20
|
+
outcome?: "success" | "partial" | "failed";
|
|
21
|
+
finalSummary?: string;
|
|
22
|
+
artifacts?: string[];
|
|
23
|
+
}
|
|
24
|
+
export interface PluginConfig {
|
|
25
|
+
storageDir?: string;
|
|
26
|
+
progressBarWidth?: number;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function buildProgressBar(current: number, total: number, width?: number): string;
|
|
2
|
+
export declare function formatOutcome(outcome: "success" | "partial" | "failed"): string;
|
|
3
|
+
export declare function formatDuration(minutes: number): string;
|
|
4
|
+
export declare function formatElapsed(startIso: string): string;
|
|
5
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/utils/format.ts"],"names":[],"mappings":"AAGA,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,CAKnF;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAY/E;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKtD;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOtD"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const FILLED = "█";
|
|
2
|
+
const EMPTY = "░";
|
|
3
|
+
export function buildProgressBar(current, total, width = 10) {
|
|
4
|
+
if (total === 0)
|
|
5
|
+
return `[${EMPTY.repeat(width)}] 0/0`;
|
|
6
|
+
const filledCount = Math.min(Math.round((current / total) * width), width);
|
|
7
|
+
const emptyCount = width - filledCount;
|
|
8
|
+
return `[${FILLED.repeat(filledCount)}${EMPTY.repeat(emptyCount)}] ${current}/${total}`;
|
|
9
|
+
}
|
|
10
|
+
export function formatOutcome(outcome) {
|
|
11
|
+
const icons = {
|
|
12
|
+
success: "✅",
|
|
13
|
+
partial: "⚠️",
|
|
14
|
+
failed: "❌",
|
|
15
|
+
};
|
|
16
|
+
const labels = {
|
|
17
|
+
success: "Completed successfully",
|
|
18
|
+
partial: "Partially completed",
|
|
19
|
+
failed: "Failed",
|
|
20
|
+
};
|
|
21
|
+
return `${icons[outcome]} ${labels[outcome]}`;
|
|
22
|
+
}
|
|
23
|
+
export function formatDuration(minutes) {
|
|
24
|
+
if (minutes < 60)
|
|
25
|
+
return `~${minutes} min`;
|
|
26
|
+
const h = Math.floor(minutes / 60);
|
|
27
|
+
const m = minutes % 60;
|
|
28
|
+
return m > 0 ? `~${h}h ${m}min` : `~${h}h`;
|
|
29
|
+
}
|
|
30
|
+
export function formatElapsed(startIso) {
|
|
31
|
+
const ms = Date.now() - new Date(startIso).getTime();
|
|
32
|
+
const totalSeconds = Math.floor(ms / 1000);
|
|
33
|
+
if (totalSeconds < 60)
|
|
34
|
+
return `${totalSeconds}s`;
|
|
35
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
36
|
+
const seconds = totalSeconds % 60;
|
|
37
|
+
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../../src/utils/format.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,GAAG,CAAC;AACnB,MAAM,KAAK,GAAG,GAAG,CAAC;AAElB,MAAM,UAAU,gBAAgB,CAAC,OAAe,EAAE,KAAa,EAAE,KAAK,GAAG,EAAE;IACzE,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;IACvD,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3E,MAAM,UAAU,GAAG,KAAK,GAAG,WAAW,CAAC;IACvC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,OAAO,IAAI,KAAK,EAAE,CAAC;AAC1F,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAyC;IACrE,MAAM,KAAK,GAAmC;QAC5C,OAAO,EAAE,GAAG;QACZ,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,GAAG;KACZ,CAAC;IACF,MAAM,MAAM,GAAmC;QAC7C,OAAO,EAAE,wBAAwB;QACjC,OAAO,EAAE,qBAAqB;QAC9B,MAAM,EAAE,QAAQ;KACjB,CAAC;IACF,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,IAAI,OAAO,MAAM,CAAC;IAC3C,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,OAAO,GAAG,EAAE,CAAC;IACvB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;IACrD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IAC3C,IAAI,YAAY,GAAG,EAAE;QAAE,OAAO,GAAG,YAAY,GAAG,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAClC,OAAO,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "claw-doing",
|
|
3
|
+
"name": "Claw Doing",
|
|
4
|
+
"description": "Real-time progress tracking for long-running OpenClaw agent tasks. See what your agent is doing — right now.",
|
|
5
|
+
"configSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"storageDir": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Custom directory for task state files. Defaults to ~/.openclaw/workspace/claw-doing/tasks/"
|
|
11
|
+
},
|
|
12
|
+
"progressBarWidth": {
|
|
13
|
+
"type": "number",
|
|
14
|
+
"description": "Width of the progress bar in characters. Defaults to 10.",
|
|
15
|
+
"minimum": 5,
|
|
16
|
+
"maximum": 20
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"additionalProperties": false
|
|
20
|
+
}
|
|
21
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claw-doing",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Real-time progress tracking for long-running OpenClaw agent tasks. See what your agent is doing — right now.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"openclaw",
|
|
8
|
+
"openclaw-plugin",
|
|
9
|
+
"agent",
|
|
10
|
+
"progress",
|
|
11
|
+
"task-tracking",
|
|
12
|
+
"long-running"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "seekcontext",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/seekcontext/claw-doing.git"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/seekcontext/claw-doing#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/seekcontext/claw-doing/issues"
|
|
23
|
+
},
|
|
24
|
+
"openclaw": {
|
|
25
|
+
"extensions": [
|
|
26
|
+
"./dist/index.js"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"main": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"import": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"bootstrap",
|
|
40
|
+
"SKILL.md",
|
|
41
|
+
"openclaw.plugin.json"
|
|
42
|
+
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc",
|
|
45
|
+
"dev": "tsc --watch",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"prepublishOnly": "npm run build"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@sinclair/typebox": "^0.34.49"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"openclaw": ">=2026.3.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"typescript": "^5.9.3",
|
|
58
|
+
"vitest": "^3.2.4"
|
|
59
|
+
}
|
|
60
|
+
}
|