@standardbeagle/dart-query 0.5.0 → 0.7.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/README.md +37 -365
- package/dist/api/dartClient.d.ts +2 -2
- package/dist/api/dartClient.d.ts.map +1 -1
- package/dist/api/dartClient.js +66 -47
- package/dist/api/dartClient.js.map +1 -1
- package/dist/batch/batchOperations.d.ts +2 -2
- package/dist/batch/batchOperations.d.ts.map +1 -1
- package/dist/batch/batchOperations.js.map +1 -1
- package/dist/batch/validateUpdates.d.ts +5 -0
- package/dist/batch/validateUpdates.d.ts.map +1 -0
- package/dist/batch/validateUpdates.js +159 -0
- package/dist/batch/validateUpdates.js.map +1 -0
- package/dist/index.js +145 -84
- package/dist/index.js.map +1 -1
- package/dist/parsers/dartql.d.ts +34 -0
- package/dist/parsers/dartql.d.ts.map +1 -1
- package/dist/parsers/dartql.js +285 -0
- package/dist/parsers/dartql.js.map +1 -1
- package/dist/tools/batch_update_tasks.d.ts.map +1 -1
- package/dist/tools/batch_update_tasks.js +3 -188
- package/dist/tools/batch_update_tasks.js.map +1 -1
- package/dist/tools/create_task.d.ts.map +1 -1
- package/dist/tools/create_task.js +1 -15
- package/dist/tools/create_task.js.map +1 -1
- package/dist/tools/execute_dartql.d.ts +4 -0
- package/dist/tools/execute_dartql.d.ts.map +1 -0
- package/dist/tools/execute_dartql.js +270 -0
- package/dist/tools/execute_dartql.js.map +1 -0
- package/dist/tools/info.d.ts.map +1 -1
- package/dist/tools/info.js +54 -5
- package/dist/tools/info.js.map +1 -1
- package/dist/tools/update_task.d.ts.map +1 -1
- package/dist/tools/update_task.js +116 -72
- package/dist/tools/update_task.js.map +1 -1
- package/dist/types/index.d.ts +71 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,90 +1,18 @@
|
|
|
1
1
|
# dart-query
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
MCP server for [Dart AI](https://dartai.com) task management, optimized for batch operations and minimal context usage.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
### The Context Rot Problem
|
|
8
|
-
|
|
9
|
-
When managing tasks in Dart AI through an LLM, you quickly run into **context rot**:
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
You: "Update all high-priority tasks in Engineering to assign them to John"
|
|
13
|
-
|
|
14
|
-
LLM: Let me list the tasks...
|
|
15
|
-
[Fetches 847 tasks, fills context window with JSON]
|
|
16
|
-
[Context limit hit before making any updates]
|
|
17
|
-
[Lost track of what we were doing]
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
**Traditional approach** (context explosion):
|
|
21
|
-
1. List all tasks → 2000+ tokens
|
|
22
|
-
2. Filter in LLM → context fills with intermediate data
|
|
23
|
-
3. Update each task individually → 50+ API calls, each response adds more context
|
|
24
|
-
4. By task #10, you've lost context of what you're doing
|
|
25
|
-
5. No way to verify results without re-fetching everything
|
|
26
|
-
|
|
27
|
-
**dart-query approach** (zero context rot):
|
|
28
|
-
1. Single DartQL query: `"dartboard = 'Engineering' AND priority = 'high'"`
|
|
29
|
-
2. Server-side batch operation updates all 50 tasks
|
|
30
|
-
3. Returns summary: "50 tasks updated in 12s"
|
|
31
|
-
4. Context usage: ~100 tokens total
|
|
32
|
-
|
|
33
|
-
### Context-Efficient Design
|
|
34
|
-
|
|
35
|
-
Every operation is designed to **minimize token usage** while maximizing capability:
|
|
36
|
-
|
|
37
|
-
| Operation | Traditional | dart-query | Token Savings |
|
|
38
|
-
|-----------|-------------|------------|---------------|
|
|
39
|
-
| Update 50 tasks | 50 API calls, ~25K tokens | 1 batch op, ~200 tokens | **99% reduction** |
|
|
40
|
-
| Import 100 tasks | 100 create calls, ~30K tokens | 1 CSV import, ~300 tokens | **99% reduction** |
|
|
41
|
-
| Find + update tasks | List all + filter + update, ~20K tokens | DartQL selector, ~150 tokens | **99% reduction** |
|
|
42
|
-
|
|
43
|
-
**Key features for context efficiency:**
|
|
44
|
-
- **Progressive disclosure**: `info` tool discovers capabilities without reading schemas
|
|
45
|
-
- **Detail levels**: Return minimal/standard/full data based on need
|
|
46
|
-
- **Batch operations**: Single operation handles hundreds of tasks
|
|
47
|
-
- **Config caching**: 5-minute cache prevents repeated fetches
|
|
48
|
-
- **DartQL language**: SQL-like selectors instead of procedural filtering
|
|
49
|
-
|
|
50
|
-
### Production Safety Without Sandbox
|
|
51
|
-
|
|
52
|
-
Dart AI has **no sandbox environment** - all operations are production. dart-query provides safety through:
|
|
53
|
-
|
|
54
|
-
- **Dry-run modes**: Preview every batch operation before execution
|
|
55
|
-
- **Validation phases**: CSV imports validate before creating anything
|
|
56
|
-
- **Confirmation flags**: Batch deletes require explicit `confirm=true`
|
|
57
|
-
- **Recoverable operations**: Deleted tasks go to trash, not permanent deletion
|
|
58
|
-
- **Error isolation**: Failed operations don't corrupt subsequent work
|
|
5
|
+
Instead of looping through tasks one-by-one (filling your context window with intermediate JSON), dart-query uses DartQL selectors and server-side batch operations to update hundreds of tasks in a single call. A 50-task update that would normally consume ~30K tokens takes ~200 tokens with zero context rot.
|
|
59
6
|
|
|
60
7
|
## Quick Start
|
|
61
8
|
|
|
62
|
-
### 1.
|
|
9
|
+
### 1. Get Your Dart AI Token
|
|
63
10
|
|
|
64
|
-
|
|
11
|
+
Visit https://app.dartai.com/?settings=account and copy your token (starts with `dsa_`).
|
|
65
12
|
|
|
66
|
-
|
|
67
|
-
npm install -g @standardbeagle/dart-query
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
**Option B: Install from source**
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
git clone https://github.com/standardbeagle/dart-query
|
|
74
|
-
cd dart-query
|
|
75
|
-
npm install
|
|
76
|
-
npm run build
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### 2. Get Your Dart AI Token
|
|
80
|
-
|
|
81
|
-
Visit https://app.dartai.com/?settings=account and copy your token (starts with `dsa_`)
|
|
82
|
-
|
|
83
|
-
### 3. Configure MCP
|
|
13
|
+
### 2. Configure MCP
|
|
84
14
|
|
|
85
|
-
**
|
|
86
|
-
|
|
87
|
-
Add to your MCP settings (e.g., `~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
15
|
+
**npx (recommended)**
|
|
88
16
|
|
|
89
17
|
```json
|
|
90
18
|
{
|
|
@@ -100,328 +28,72 @@ Add to your MCP settings (e.g., `~/Library/Application Support/Claude/claude_des
|
|
|
100
28
|
}
|
|
101
29
|
```
|
|
102
30
|
|
|
103
|
-
**
|
|
104
|
-
|
|
105
|
-
```json
|
|
106
|
-
{
|
|
107
|
-
"mcpServers": {
|
|
108
|
-
"dart-query": {
|
|
109
|
-
"command": "node",
|
|
110
|
-
"args": ["/absolute/path/to/dart-query/dist/index.js"],
|
|
111
|
-
"env": {
|
|
112
|
-
"DART_TOKEN": "dsa_your_token_here"
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
**Option C: Using SLOP-MCP for dynamic management**
|
|
31
|
+
**SLOP-MCP (v0.10.0+)**
|
|
120
32
|
|
|
121
33
|
```bash
|
|
122
|
-
# With npm package
|
|
123
34
|
slop register dart-query \
|
|
124
35
|
--command npx \
|
|
125
36
|
--args "-y" "@standardbeagle/dart-query" \
|
|
126
37
|
--env DART_TOKEN=dsa_your_token_here \
|
|
127
38
|
--scope user
|
|
128
|
-
|
|
129
|
-
# With local installation
|
|
130
|
-
slop register dart-query \
|
|
131
|
-
--command node \
|
|
132
|
-
--args dist/index.js \
|
|
133
|
-
--env DART_TOKEN=dsa_your_token_here \
|
|
134
|
-
--scope user
|
|
135
39
|
```
|
|
136
40
|
|
|
137
|
-
###
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
// Get workspace config
|
|
141
|
-
get_config({})
|
|
41
|
+
### 3. Verify
|
|
142
42
|
|
|
143
|
-
|
|
43
|
+
```
|
|
144
44
|
info({ level: "overview" })
|
|
145
45
|
```
|
|
146
46
|
|
|
147
|
-
###
|
|
47
|
+
### 4. Example: Batch Update
|
|
148
48
|
|
|
149
49
|
```typescript
|
|
150
|
-
//
|
|
151
|
-
create_task({
|
|
152
|
-
title: "Test dart-query MCP",
|
|
153
|
-
dartboard: "Personal/test",
|
|
154
|
-
priority: "high"
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
// Batch update multiple tasks (dry run first!)
|
|
50
|
+
// Preview first
|
|
158
51
|
batch_update_tasks({
|
|
159
|
-
selector: "dartboard = '
|
|
52
|
+
selector: "dartboard = 'Engineering' AND priority = 'high'",
|
|
160
53
|
updates: { status: "Doing" },
|
|
161
|
-
dry_run: true
|
|
54
|
+
dry_run: true
|
|
162
55
|
})
|
|
163
56
|
|
|
164
|
-
// Execute
|
|
57
|
+
// Execute
|
|
165
58
|
batch_update_tasks({
|
|
166
|
-
selector: "dartboard = '
|
|
59
|
+
selector: "dartboard = 'Engineering' AND priority = 'high'",
|
|
167
60
|
updates: { status: "Doing" },
|
|
168
61
|
dry_run: false
|
|
169
62
|
})
|
|
170
|
-
|
|
171
|
-
// Clean up
|
|
172
|
-
batch_delete_tasks({
|
|
173
|
-
selector: "dartboard = 'Personal/test'",
|
|
174
|
-
dry_run: false,
|
|
175
|
-
confirm: true // Required safety flag
|
|
176
|
-
})
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## Core Features
|
|
180
|
-
|
|
181
|
-
### 🔍 **Progressive Discovery**
|
|
182
|
-
Start with `info` tool to explore capabilities without loading all schemas. Navigate overview → group → tool with increasing detail.
|
|
183
|
-
|
|
184
|
-
### 🎯 **DartQL Query Language**
|
|
185
|
-
SQL-like WHERE clause syntax for powerful batch operations:
|
|
186
|
-
```sql
|
|
187
|
-
dartboard = 'Engineering' AND priority = 'high' AND tags CONTAINS 'bug'
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### 📊 **CSV Bulk Import**
|
|
191
|
-
Import hundreds of tasks from CSV with validation, error recovery, and fuzzy matching:
|
|
192
|
-
- Validate phase catches errors before creating anything
|
|
193
|
-
- Parallel import with configurable concurrency
|
|
194
|
-
- Continue-on-error mode for resilience
|
|
195
|
-
|
|
196
|
-
### ⚡ **Batch Operations**
|
|
197
|
-
Update or delete hundreds of tasks in a single operation:
|
|
198
|
-
- Server-side execution (no context rot)
|
|
199
|
-
- Dry-run preview mode
|
|
200
|
-
- Parallel processing with rate limiting
|
|
201
|
-
|
|
202
|
-
### 💾 **Context Efficiency**
|
|
203
|
-
- Detail levels (minimal/standard/full)
|
|
204
|
-
- 5-minute config cache
|
|
205
|
-
- Token-optimized responses
|
|
206
|
-
- Progressive disclosure of capabilities
|
|
207
|
-
|
|
208
|
-
### 🛡️ **Production Safety**
|
|
209
|
-
- No sandbox: all operations are production
|
|
210
|
-
- Dry-run modes for batch operations
|
|
211
|
-
- Validation phases for CSV imports
|
|
212
|
-
- Confirmation flags for destructive operations
|
|
213
|
-
- Recoverable deletions (tasks → trash)
|
|
214
|
-
|
|
215
|
-
## Tool Groups
|
|
216
|
-
|
|
217
|
-
| Group | Tools | Use Case |
|
|
218
|
-
|-------|-------|----------|
|
|
219
|
-
| **Discovery** | `info`, `get_config` | Explore capabilities, get workspace config |
|
|
220
|
-
| **Task CRUD** | `create_task`, `get_task`, `update_task`, `delete_task`, `add_task_comment` | Single task operations |
|
|
221
|
-
| **Task Query** | `list_tasks`, `search_tasks` | Find tasks with filters or full-text search |
|
|
222
|
-
| **Batch Operations** | `batch_update_tasks`, `batch_delete_tasks`, `get_batch_status` | Bulk operations on hundreds of tasks |
|
|
223
|
-
| **CSV Import** | `import_tasks_csv` | Bulk create from CSV files |
|
|
224
|
-
| **Documents** | `list_docs`, `create_doc`, `get_doc`, `update_doc`, `delete_doc` | Document management |
|
|
225
|
-
|
|
226
|
-
## Common Use Cases
|
|
227
|
-
|
|
228
|
-
### Bulk Task Management
|
|
229
|
-
```typescript
|
|
230
|
-
// Update all overdue high-priority tasks
|
|
231
|
-
batch_update_tasks({
|
|
232
|
-
selector: "due_at < '2026-01-18' AND priority = 'high' AND status != 'Done'",
|
|
233
|
-
updates: { priority: "critical", assignees: ["john@company.com"] },
|
|
234
|
-
dry_run: true // Preview first!
|
|
235
|
-
})
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Project Cleanup
|
|
239
|
-
```typescript
|
|
240
|
-
// Archive completed tasks from Q4 2025
|
|
241
|
-
batch_update_tasks({
|
|
242
|
-
selector: "completed_at >= '2025-10-01' AND completed_at < '2026-01-01'",
|
|
243
|
-
updates: { dartboard: "Archive" },
|
|
244
|
-
dry_run: false,
|
|
245
|
-
concurrency: 10
|
|
246
|
-
})
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### CSV Migration
|
|
250
|
-
```typescript
|
|
251
|
-
// Import tasks from external system
|
|
252
|
-
import_tasks_csv({
|
|
253
|
-
csv_file_path: "./jira-export.csv",
|
|
254
|
-
dartboard: "Engineering",
|
|
255
|
-
column_mapping: {
|
|
256
|
-
"Issue Summary": "title",
|
|
257
|
-
"Assignee Email": "assignee",
|
|
258
|
-
"Priority": "priority"
|
|
259
|
-
},
|
|
260
|
-
validate_only: true // Validate first!
|
|
261
|
-
})
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
### Search and Update
|
|
265
|
-
```typescript
|
|
266
|
-
// Find all authentication-related tasks
|
|
267
|
-
const results = search_tasks({
|
|
268
|
-
query: "authentication oauth security",
|
|
269
|
-
dartboard: "Engineering",
|
|
270
|
-
limit: 20
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
// Update them in batch
|
|
274
|
-
batch_update_tasks({
|
|
275
|
-
selector: "tags CONTAINS 'security' AND title LIKE '%auth%'",
|
|
276
|
-
updates: { priority: "high" }
|
|
277
|
-
})
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
## Documentation
|
|
281
|
-
|
|
282
|
-
📖 **[Complete Tool Documentation →](./TOOLS.md)**
|
|
283
|
-
|
|
284
|
-
Detailed documentation for all tools including:
|
|
285
|
-
- Full parameter references
|
|
286
|
-
- Return value schemas
|
|
287
|
-
- How-to guides for common workflows
|
|
288
|
-
- Use case examples
|
|
289
|
-
- DartQL syntax reference
|
|
290
|
-
- CSV import formats
|
|
291
|
-
- Error handling strategies
|
|
292
|
-
- Performance optimization tips
|
|
293
|
-
|
|
294
|
-
## Production Safety Checklist
|
|
295
|
-
|
|
296
|
-
**Before ANY batch operation:**
|
|
297
|
-
- [ ] Use `dry_run: true` and review preview
|
|
298
|
-
- [ ] Verify selector matches ONLY intended tasks
|
|
299
|
-
- [ ] Test with small dataset first (< 10 tasks)
|
|
300
|
-
- [ ] Have rollback plan (tasks go to trash, recoverable)
|
|
301
|
-
|
|
302
|
-
**Before CSV import:**
|
|
303
|
-
- [ ] Use `validate_only: true` and fix all errors
|
|
304
|
-
- [ ] Test with 5-10 rows first
|
|
305
|
-
- [ ] Verify column mapping is correct
|
|
306
|
-
- [ ] Check references exist in workspace (`get_config`)
|
|
307
|
-
|
|
308
|
-
**Before batch delete:**
|
|
309
|
-
- [ ] Triple-check selector specificity
|
|
310
|
-
- [ ] Understand tasks move to trash (recoverable)
|
|
311
|
-
- [ ] Set `confirm: true` (required safety flag)
|
|
312
|
-
|
|
313
|
-
## Performance Metrics
|
|
314
|
-
|
|
315
|
-
Tested with production Dart API:
|
|
316
|
-
|
|
317
|
-
| Operation | Tasks | Time | Throughput |
|
|
318
|
-
|-----------|-------|------|------------|
|
|
319
|
-
| CSV Import | 41 tasks | 17.4s | 2.4 tasks/sec |
|
|
320
|
-
| Batch Update | 75 tasks | 22s | 3.4 tasks/sec |
|
|
321
|
-
| Batch Delete | 165 tasks | 37s | 4.5 tasks/sec |
|
|
322
|
-
| Single CRUD | 1 task | <2s | - |
|
|
323
|
-
|
|
324
|
-
*Concurrency: 10-20 parallel operations, production rate limits observed*
|
|
325
|
-
|
|
326
|
-
## Troubleshooting
|
|
327
|
-
|
|
328
|
-
### Authentication Issues
|
|
329
|
-
```
|
|
330
|
-
Error: Invalid DART_TOKEN
|
|
331
|
-
```
|
|
332
|
-
**Solution**: Ensure token starts with `dsa_` and get fresh token from https://app.dartai.com/?settings=account
|
|
333
|
-
|
|
334
|
-
### Rate Limiting (429)
|
|
335
|
-
```
|
|
336
|
-
Error: Rate limit exceeded
|
|
337
63
|
```
|
|
338
|
-
**Solution**: Reduce `concurrency` parameter (default: 5, try: 2-3). Automatic retry with exponential backoff.
|
|
339
64
|
|
|
340
|
-
|
|
341
|
-
```
|
|
342
|
-
Error: Row 3, column 'priority': Invalid priority: "5". Available: critical, high, medium, low
|
|
343
|
-
```
|
|
344
|
-
**Solution**: Use `validate_only: true` to see all errors. Check available values with `get_config()`.
|
|
345
|
-
|
|
346
|
-
### DartQL Syntax Errors
|
|
347
|
-
```
|
|
348
|
-
Error: Unknown field: priorty. Did you mean: priority?
|
|
349
|
-
```
|
|
350
|
-
**Solution**: Use fuzzy match suggestions. Reference field list with `info({ level: "tool", target: "batch_update_tasks" })`.
|
|
351
|
-
|
|
352
|
-
**See [TOOLS.md](./TOOLS.md) for comprehensive troubleshooting guide.**
|
|
65
|
+
## Tools
|
|
353
66
|
|
|
354
|
-
|
|
67
|
+
| Group | Tools | Purpose |
|
|
68
|
+
|-------|-------|---------|
|
|
69
|
+
| Discovery | `info`, `get_config` | Explore capabilities, workspace config |
|
|
70
|
+
| Task CRUD | `create_task`, `get_task`, `update_task`, `delete_task`, `add_task_comment` | Single task operations |
|
|
71
|
+
| Query | `list_tasks`, `search_tasks` | Find tasks with filters or full-text search |
|
|
72
|
+
| Batch | `batch_update_tasks`, `batch_delete_tasks`, `get_batch_status` | Bulk operations with DartQL selectors |
|
|
73
|
+
| Import | `import_tasks_csv` | Bulk create from CSV with validation |
|
|
74
|
+
| Docs | `list_docs`, `create_doc`, `get_doc`, `update_doc`, `delete_doc` | Document management |
|
|
355
75
|
|
|
356
|
-
|
|
357
|
-
# Install dependencies
|
|
358
|
-
npm install
|
|
76
|
+
See **[TOOLS.md](./TOOLS.md)** for full parameter references, DartQL syntax, and CSV import format.
|
|
359
77
|
|
|
360
|
-
|
|
361
|
-
npm run build
|
|
78
|
+
## DartQL Selectors
|
|
362
79
|
|
|
363
|
-
|
|
364
|
-
npm run typecheck
|
|
80
|
+
SQL-like WHERE clauses for targeting tasks in batch operations:
|
|
365
81
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
### Project Structure
|
|
371
|
-
```
|
|
372
|
-
src/
|
|
373
|
-
├── index.ts # MCP server entry point
|
|
374
|
-
├── tools/ # Tool implementations (info, CRUD, batch, import)
|
|
375
|
-
├── api/dartClient.ts # Dart API wrapper with retry logic
|
|
376
|
-
├── parsers/ # DartQL and CSV parsers
|
|
377
|
-
├── cache/configCache.ts # 5-minute config cache
|
|
378
|
-
├── batch/ # Batch operation tracking
|
|
379
|
-
└── types/index.ts # TypeScript interfaces
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
## Design Philosophy
|
|
383
|
-
|
|
384
|
-
1. **Context efficiency first**: Every feature minimizes token usage
|
|
385
|
-
2. **Production safety**: Dry-run, validation, confirmation flags
|
|
386
|
-
3. **Progressive disclosure**: Discover capabilities without overwhelming schemas
|
|
387
|
-
4. **Zero context rot**: Batch operations prevent context pollution
|
|
388
|
-
5. **Fail-safe defaults**: `dry_run: true`, `validate_only: true` by default
|
|
389
|
-
|
|
390
|
-
## Comparison: Traditional vs dart-query
|
|
391
|
-
|
|
392
|
-
### Update 50 Tasks (Traditional LLM approach)
|
|
393
|
-
```
|
|
394
|
-
1. list_tasks() → Returns 50 task objects (~15,000 tokens)
|
|
395
|
-
2. For each task:
|
|
396
|
-
- update_task(task1) → ~300 tokens
|
|
397
|
-
- update_task(task2) → ~300 tokens
|
|
398
|
-
- ... (50 iterations)
|
|
399
|
-
3. Total: ~30,000 tokens, 50 API calls, context window exhausted
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
### Update 50 Tasks (dart-query)
|
|
403
|
-
```
|
|
404
|
-
1. batch_update_tasks({
|
|
405
|
-
selector: "dartboard = 'X' AND priority = 'high'",
|
|
406
|
-
updates: { assignee: "john@company.com" }
|
|
407
|
-
})
|
|
408
|
-
2. Total: ~200 tokens, 1 API call, zero context rot
|
|
82
|
+
```sql
|
|
83
|
+
dartboard = 'Engineering' AND priority = 'high' AND tags CONTAINS 'bug'
|
|
84
|
+
due_at < '2026-01-18' AND status != 'Done'
|
|
85
|
+
title LIKE '%auth%'
|
|
409
86
|
```
|
|
410
87
|
|
|
411
|
-
|
|
412
|
-
**Time savings: 90%**
|
|
413
|
-
**Context rot: Eliminated**
|
|
88
|
+
## Safety
|
|
414
89
|
|
|
415
|
-
|
|
90
|
+
All Dart AI operations are production (no sandbox). dart-query provides:
|
|
416
91
|
|
|
417
|
-
-
|
|
418
|
-
-
|
|
419
|
-
-
|
|
92
|
+
- **Dry-run mode** on all batch operations — preview before executing
|
|
93
|
+
- **Validation phase** for CSV imports — catch errors before creating anything
|
|
94
|
+
- **Confirmation flag** (`confirm: true`) required for batch deletes
|
|
95
|
+
- **Recoverable deletes** — tasks move to trash, not permanent deletion
|
|
420
96
|
|
|
421
97
|
## License
|
|
422
98
|
|
|
423
99
|
MIT
|
|
424
|
-
|
|
425
|
-
---
|
|
426
|
-
|
|
427
|
-
**Built for production use. Tested with live Dart AI workspace managing 2000+ tasks across 67 dartboards.**
|
package/dist/api/dartClient.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DartTask, DartConfig, DartDoc, CreateTaskInput,
|
|
1
|
+
import { DartTask, DartConfig, DartDoc, CreateTaskInput, ListTasksInput } from '../types/index.js';
|
|
2
2
|
export interface DartClientConfig {
|
|
3
3
|
token: string;
|
|
4
4
|
baseUrl?: string;
|
|
@@ -17,7 +17,7 @@ export declare class DartClient {
|
|
|
17
17
|
}>;
|
|
18
18
|
private mapTaskResponse;
|
|
19
19
|
getTask(dartId: string): Promise<DartTask>;
|
|
20
|
-
updateTask(
|
|
20
|
+
updateTask(dartId: string, updates: Partial<DartTask>): Promise<DartTask>;
|
|
21
21
|
deleteTask(dartId: string): Promise<{
|
|
22
22
|
success: boolean;
|
|
23
23
|
dart_id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dartClient.d.ts","sourceRoot":"","sources":["../../src/api/dartClient.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,QAAQ,EACR,UAAU,EACV,OAAO,EACP,eAAe,EACf,
|
|
1
|
+
{"version":3,"file":"dartClient.d.ts","sourceRoot":"","sources":["../../src/api/dartClient.ts"],"names":[],"mappings":"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"}
|
package/dist/api/dartClient.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import pRetry from 'p-retry';
|
|
2
2
|
import { DartAPIError, } from '../types/index.js';
|
|
3
|
+
const STATUS_LABELS = {
|
|
4
|
+
400: 'Bad Request',
|
|
5
|
+
401: 'Unauthorized',
|
|
6
|
+
403: 'Forbidden',
|
|
7
|
+
404: 'Not Found',
|
|
8
|
+
429: 'Rate Limited',
|
|
9
|
+
500: 'Server Error',
|
|
10
|
+
502: 'Bad Gateway',
|
|
11
|
+
503: 'Service Unavailable',
|
|
12
|
+
};
|
|
3
13
|
export class DartClient {
|
|
4
14
|
token;
|
|
5
15
|
baseUrl;
|
|
@@ -74,40 +84,42 @@ export class DartClient {
|
|
|
74
84
|
});
|
|
75
85
|
}
|
|
76
86
|
async handleErrorResponse(response) {
|
|
77
|
-
let
|
|
78
|
-
let
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
let responseBody;
|
|
88
|
+
let errorDetail = '';
|
|
89
|
+
try {
|
|
90
|
+
const text = await response.text();
|
|
91
|
+
if (text) {
|
|
92
|
+
const contentType = response.headers.get('content-type');
|
|
93
|
+
if (contentType?.includes('application/json')) {
|
|
94
|
+
const parsed = JSON.parse(text);
|
|
95
|
+
responseBody = parsed;
|
|
96
|
+
errorDetail =
|
|
97
|
+
parsed.error?.message ||
|
|
98
|
+
parsed.detail ||
|
|
99
|
+
parsed.message ||
|
|
100
|
+
(typeof parsed.error === 'string' ? parsed.error : '') ||
|
|
101
|
+
'';
|
|
102
|
+
if (!errorDetail && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
103
|
+
const fields = Object.entries(parsed)
|
|
104
|
+
.filter(([, v]) => v !== null && v !== undefined)
|
|
105
|
+
.map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(', ') : v}`)
|
|
106
|
+
.slice(0, 5);
|
|
107
|
+
if (fields.length > 0) {
|
|
108
|
+
errorDetail = fields.join('; ');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
errorDetail = text.slice(0, 500);
|
|
86
114
|
}
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
115
|
}
|
|
90
116
|
}
|
|
91
|
-
|
|
92
|
-
case 400:
|
|
93
|
-
throw new DartAPIError(`Bad Request: ${errorMessage}`, 400, errorData);
|
|
94
|
-
case 401:
|
|
95
|
-
throw new DartAPIError(`Unauthorized: Invalid or expired token. ${errorMessage}`, 401, errorData);
|
|
96
|
-
case 403:
|
|
97
|
-
throw new DartAPIError(`Forbidden: Insufficient permissions. ${errorMessage}`, 403, errorData);
|
|
98
|
-
case 404:
|
|
99
|
-
throw new DartAPIError(`Not Found: ${errorMessage}`, 404, errorData);
|
|
100
|
-
case 429:
|
|
101
|
-
throw new DartAPIError(`Rate Limit Exceeded: ${errorMessage}`, 429, errorData);
|
|
102
|
-
case 500:
|
|
103
|
-
throw new DartAPIError(`Internal Server Error: ${errorMessage}`, 500, errorData);
|
|
104
|
-
case 502:
|
|
105
|
-
throw new DartAPIError(`Bad Gateway: ${errorMessage}`, 502, errorData);
|
|
106
|
-
case 503:
|
|
107
|
-
throw new DartAPIError(`Service Unavailable: ${errorMessage}`, 503, errorData);
|
|
108
|
-
default:
|
|
109
|
-
throw new DartAPIError(`HTTP ${response.status}: ${errorMessage}`, response.status, errorData);
|
|
117
|
+
catch {
|
|
110
118
|
}
|
|
119
|
+
const status = response.status;
|
|
120
|
+
const statusLabel = STATUS_LABELS[status] || `HTTP ${status}`;
|
|
121
|
+
const message = errorDetail || response.statusText || 'No details available';
|
|
122
|
+
throw new DartAPIError(`${statusLabel}: ${message}`, status, responseBody);
|
|
111
123
|
}
|
|
112
124
|
async getConfig() {
|
|
113
125
|
return this.request('GET', '/config');
|
|
@@ -141,16 +153,20 @@ export class DartClient {
|
|
|
141
153
|
apiInput.startAt = input.start_at;
|
|
142
154
|
if (input.parent_task)
|
|
143
155
|
apiInput.parentId = input.parent_task;
|
|
156
|
+
const taskRelationships = {};
|
|
144
157
|
if (input.subtask_ids !== undefined)
|
|
145
|
-
|
|
158
|
+
taskRelationships.subtaskIds = input.subtask_ids;
|
|
146
159
|
if (input.blocker_ids !== undefined)
|
|
147
|
-
|
|
160
|
+
taskRelationships.blockerIds = input.blocker_ids;
|
|
148
161
|
if (input.blocking_ids !== undefined)
|
|
149
|
-
|
|
162
|
+
taskRelationships.blockingIds = input.blocking_ids;
|
|
150
163
|
if (input.duplicate_ids !== undefined)
|
|
151
|
-
|
|
164
|
+
taskRelationships.duplicateIds = input.duplicate_ids;
|
|
152
165
|
if (input.related_ids !== undefined)
|
|
153
|
-
|
|
166
|
+
taskRelationships.relatedIds = input.related_ids;
|
|
167
|
+
if (Object.keys(taskRelationships).length > 0) {
|
|
168
|
+
apiInput.taskRelationships = taskRelationships;
|
|
169
|
+
}
|
|
154
170
|
const response = await this.request('POST', '/tasks', { item: apiInput });
|
|
155
171
|
return this.mapTaskResponse(response.item);
|
|
156
172
|
}
|
|
@@ -212,16 +228,15 @@ export class DartClient {
|
|
|
212
228
|
const response = await this.request('GET', `/tasks/${encodeURIComponent(dartId.trim())}`);
|
|
213
229
|
return this.mapTaskResponse(response.item);
|
|
214
230
|
}
|
|
215
|
-
async updateTask(
|
|
216
|
-
if (!
|
|
231
|
+
async updateTask(dartId, updates) {
|
|
232
|
+
if (!dartId || typeof dartId !== 'string' || dartId.trim() === '') {
|
|
217
233
|
throw new DartAPIError('dart_id is required and must be a non-empty string', 400);
|
|
218
234
|
}
|
|
219
|
-
if (!
|
|
220
|
-
throw new DartAPIError('
|
|
235
|
+
if (!updates || typeof updates !== 'object' || Object.keys(updates).length === 0) {
|
|
236
|
+
throw new DartAPIError('No fields to update', 400);
|
|
221
237
|
}
|
|
222
|
-
const { dart_id, updates } = input;
|
|
223
238
|
const apiUpdates = {
|
|
224
|
-
id:
|
|
239
|
+
id: dartId.trim(),
|
|
225
240
|
};
|
|
226
241
|
if (updates.title !== undefined)
|
|
227
242
|
apiUpdates.title = updates.title;
|
|
@@ -245,17 +260,21 @@ export class DartClient {
|
|
|
245
260
|
apiUpdates.startAt = updates.start_at;
|
|
246
261
|
if (updates.parent_task !== undefined)
|
|
247
262
|
apiUpdates.parentId = updates.parent_task;
|
|
263
|
+
const taskRelationships = {};
|
|
248
264
|
if (updates.subtask_ids !== undefined)
|
|
249
|
-
|
|
265
|
+
taskRelationships.subtaskIds = updates.subtask_ids;
|
|
250
266
|
if (updates.blocker_ids !== undefined)
|
|
251
|
-
|
|
267
|
+
taskRelationships.blockerIds = updates.blocker_ids;
|
|
252
268
|
if (updates.blocking_ids !== undefined)
|
|
253
|
-
|
|
269
|
+
taskRelationships.blockingIds = updates.blocking_ids;
|
|
254
270
|
if (updates.duplicate_ids !== undefined)
|
|
255
|
-
|
|
271
|
+
taskRelationships.duplicateIds = updates.duplicate_ids;
|
|
256
272
|
if (updates.related_ids !== undefined)
|
|
257
|
-
|
|
258
|
-
|
|
273
|
+
taskRelationships.relatedIds = updates.related_ids;
|
|
274
|
+
if (Object.keys(taskRelationships).length > 0) {
|
|
275
|
+
apiUpdates.taskRelationships = taskRelationships;
|
|
276
|
+
}
|
|
277
|
+
const response = await this.request('PUT', `/tasks/${encodeURIComponent(dartId.trim())}`, { item: apiUpdates });
|
|
259
278
|
return this.mapTaskResponse(response.item);
|
|
260
279
|
}
|
|
261
280
|
async deleteTask(dartId) {
|