clawdo 1.0.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 +312 -0
- package/dist/db.js +906 -0
- package/dist/errors.js +48 -0
- package/dist/inbox.js +122 -0
- package/dist/index.js +759 -0
- package/dist/parser.js +78 -0
- package/dist/render.js +238 -0
- package/dist/sanitize.js +146 -0
- package/dist/types.js +4 -0
- package/package.json +60 -0
package/dist/errors.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClawdoError - Error class with stable error codes for agent error handling.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* throw new ClawdoError(
|
|
7
|
+
* 'TASK_NOT_FOUND',
|
|
8
|
+
* `Task not found: ${id}`,
|
|
9
|
+
* { id, suggestions: findSimilarIds(id) }
|
|
10
|
+
* );
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* @example Agent error handling
|
|
14
|
+
* ```typescript
|
|
15
|
+
* try {
|
|
16
|
+
* db.completeTask(id, 'agent');
|
|
17
|
+
* } catch (err) {
|
|
18
|
+
* if (err.code === 'TASK_BLOCKED') {
|
|
19
|
+
* // Handle blocked task
|
|
20
|
+
* } else if (err.code === 'TASK_NOT_CONFIRMED') {
|
|
21
|
+
* // Prompt human to confirm
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export class ClawdoError extends Error {
|
|
27
|
+
code;
|
|
28
|
+
context;
|
|
29
|
+
constructor(code, message, context) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.context = context;
|
|
33
|
+
this.name = 'ClawdoError';
|
|
34
|
+
// Ensure proper prototype chain for instanceof checks
|
|
35
|
+
Object.setPrototypeOf(this, ClawdoError.prototype);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Returns a JSON representation of the error for --json output.
|
|
39
|
+
*/
|
|
40
|
+
toJSON() {
|
|
41
|
+
return {
|
|
42
|
+
error: true,
|
|
43
|
+
code: this.code,
|
|
44
|
+
message: this.message,
|
|
45
|
+
context: this.context
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
package/dist/inbox.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-facing inbox - structured JSON with security wrapper
|
|
3
|
+
*/
|
|
4
|
+
import { wrapForLLM } from './sanitize.js';
|
|
5
|
+
export function generateInbox(db) {
|
|
6
|
+
const autoExecutionEnabled = db.getConfig('auto_execution_enabled') === 'true';
|
|
7
|
+
const tasksCompleted4h = db.countCompletedInLast(4);
|
|
8
|
+
// OPTIMIZATION: Single query + JS partitioning (fast for <5k tasks)
|
|
9
|
+
// For >10k tasks, push filtering to SQL with separate queries per category
|
|
10
|
+
// Trade-off: 1 query + N iterations (current) vs N queries + 0 iterations
|
|
11
|
+
const allActiveTasks = db.listTasks({ status: ['todo', 'in_progress', 'proposed'] });
|
|
12
|
+
const now = new Date().toISOString().split('T')[0];
|
|
13
|
+
const staleThreshold = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
|
|
14
|
+
// Partition tasks in a single pass
|
|
15
|
+
const result = {
|
|
16
|
+
meta: {
|
|
17
|
+
autoExecutionEnabled,
|
|
18
|
+
tasksCompleted4h,
|
|
19
|
+
},
|
|
20
|
+
autoReady: [],
|
|
21
|
+
autoNotifyReady: [],
|
|
22
|
+
urgent: [],
|
|
23
|
+
overdue: [],
|
|
24
|
+
proposed: [],
|
|
25
|
+
stale: [],
|
|
26
|
+
blocked: [],
|
|
27
|
+
};
|
|
28
|
+
for (const task of allActiveTasks) {
|
|
29
|
+
// Categorize by status
|
|
30
|
+
if (task.status === 'proposed') {
|
|
31
|
+
result.proposed.push(task);
|
|
32
|
+
}
|
|
33
|
+
// Urgent tasks (any status except archived/done)
|
|
34
|
+
if (task.urgency === 'now' && (task.status === 'todo' || task.status === 'in_progress')) {
|
|
35
|
+
result.urgent.push(task);
|
|
36
|
+
}
|
|
37
|
+
// Overdue tasks
|
|
38
|
+
if (task.dueDate && task.dueDate < now && (task.status === 'todo' || task.status === 'in_progress')) {
|
|
39
|
+
result.overdue.push(task);
|
|
40
|
+
}
|
|
41
|
+
// Blocked tasks
|
|
42
|
+
if (task.blockedBy && (task.status === 'todo' || task.status === 'in_progress')) {
|
|
43
|
+
result.blocked.push(task);
|
|
44
|
+
}
|
|
45
|
+
// Stale tasks (in_progress > 24h)
|
|
46
|
+
if (task.status === 'in_progress' && task.startedAt && task.startedAt < staleThreshold) {
|
|
47
|
+
result.stale.push(task);
|
|
48
|
+
}
|
|
49
|
+
// Auto-ready tasks (todo status, not blocked)
|
|
50
|
+
if (task.status === 'todo' && !task.blockedBy) {
|
|
51
|
+
if (task.autonomy === 'auto') {
|
|
52
|
+
result.autoReady.push(task);
|
|
53
|
+
}
|
|
54
|
+
else if (task.autonomy === 'auto-notify') {
|
|
55
|
+
result.autoNotifyReady.push(task);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
export function formatInboxJSON(inbox) {
|
|
62
|
+
const json = JSON.stringify(inbox, null, 2);
|
|
63
|
+
return wrapForLLM(json);
|
|
64
|
+
}
|
|
65
|
+
export function formatInboxMarkdown(inbox) {
|
|
66
|
+
const lines = [];
|
|
67
|
+
lines.push('# Todo Inbox\n');
|
|
68
|
+
lines.push('## Meta');
|
|
69
|
+
lines.push(`- Auto execution: ${inbox.meta.autoExecutionEnabled ? 'enabled' : 'disabled'}`);
|
|
70
|
+
lines.push(`- Tasks completed (4h): ${inbox.meta.tasksCompleted4h}\n`);
|
|
71
|
+
if (inbox.urgent.length > 0) {
|
|
72
|
+
lines.push(`## Urgent (${inbox.urgent.length})`);
|
|
73
|
+
for (const task of inbox.urgent) {
|
|
74
|
+
lines.push(`- [${task.id}] ${task.text} (${task.autonomy})`);
|
|
75
|
+
}
|
|
76
|
+
lines.push('');
|
|
77
|
+
}
|
|
78
|
+
if (inbox.overdue.length > 0) {
|
|
79
|
+
lines.push(`## Overdue (${inbox.overdue.length})`);
|
|
80
|
+
for (const task of inbox.overdue) {
|
|
81
|
+
lines.push(`- [${task.id}] ${task.text} - due ${task.dueDate}`);
|
|
82
|
+
}
|
|
83
|
+
lines.push('');
|
|
84
|
+
}
|
|
85
|
+
if (inbox.autoReady.length > 0) {
|
|
86
|
+
lines.push(`## Auto Ready (${inbox.autoReady.length})`);
|
|
87
|
+
for (const task of inbox.autoReady) {
|
|
88
|
+
lines.push(`- [${task.id}] ${task.text}`);
|
|
89
|
+
}
|
|
90
|
+
lines.push('');
|
|
91
|
+
}
|
|
92
|
+
if (inbox.autoNotifyReady.length > 0) {
|
|
93
|
+
lines.push(`## Auto-Notify Ready (${inbox.autoNotifyReady.length})`);
|
|
94
|
+
for (const task of inbox.autoNotifyReady) {
|
|
95
|
+
lines.push(`- [${task.id}] ${task.text}`);
|
|
96
|
+
}
|
|
97
|
+
lines.push('');
|
|
98
|
+
}
|
|
99
|
+
if (inbox.proposed.length > 0) {
|
|
100
|
+
lines.push(`## Proposed (${inbox.proposed.length})`);
|
|
101
|
+
for (const task of inbox.proposed) {
|
|
102
|
+
lines.push(`- [${task.id}] ${task.text} (${task.urgency})`);
|
|
103
|
+
}
|
|
104
|
+
lines.push('');
|
|
105
|
+
}
|
|
106
|
+
if (inbox.stale.length > 0) {
|
|
107
|
+
lines.push(`## Stale (in progress >24h) (${inbox.stale.length})`);
|
|
108
|
+
for (const task of inbox.stale) {
|
|
109
|
+
const startedAgo = task.startedAt ? `started ${task.startedAt}` : '';
|
|
110
|
+
lines.push(`- [${task.id}] ${task.text} ${startedAgo}`);
|
|
111
|
+
}
|
|
112
|
+
lines.push('');
|
|
113
|
+
}
|
|
114
|
+
if (inbox.blocked.length > 0) {
|
|
115
|
+
lines.push(`## Blocked (${inbox.blocked.length})`);
|
|
116
|
+
for (const task of inbox.blocked) {
|
|
117
|
+
lines.push(`- [${task.id}] ${task.text} - blocked by ${task.blockedBy}`);
|
|
118
|
+
}
|
|
119
|
+
lines.push('');
|
|
120
|
+
}
|
|
121
|
+
return lines.join('\n');
|
|
122
|
+
}
|