moltedopus 2.0.1 → 2.0.2
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/lib/heartbeat.js +123 -6
- package/package.json +1 -1
package/lib/heartbeat.js
CHANGED
|
@@ -1136,23 +1136,140 @@ async function cmdRooms() {
|
|
|
1136
1136
|
}
|
|
1137
1137
|
|
|
1138
1138
|
// ============================================================
|
|
1139
|
-
// SUBCOMMAND: tasks ROOM_ID
|
|
1139
|
+
// SUBCOMMAND: tasks ROOM_ID [--all] [--json] [--status=X]
|
|
1140
1140
|
// ============================================================
|
|
1141
1141
|
|
|
1142
1142
|
async function cmdTasks(argv) {
|
|
1143
|
-
const
|
|
1143
|
+
const positional = argv.filter(a => !a.startsWith('--'));
|
|
1144
|
+
const args = parseArgs(argv);
|
|
1145
|
+
const roomId = positional[0];
|
|
1144
1146
|
if (!roomId) {
|
|
1145
|
-
console.error('Usage: moltedopus tasks ROOM_ID');
|
|
1147
|
+
console.error('Usage: moltedopus tasks ROOM_ID [--all] [--json] [--status=todo|in_progress|done]');
|
|
1146
1148
|
console.error('Tasks are room-scoped. Provide a room ID.');
|
|
1147
1149
|
process.exit(1);
|
|
1148
1150
|
}
|
|
1149
1151
|
const result = await getRoomTasks(roomId);
|
|
1150
|
-
if (result) {
|
|
1151
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1152
|
-
} else {
|
|
1152
|
+
if (!result) {
|
|
1153
1153
|
console.error('Failed to fetch tasks');
|
|
1154
1154
|
process.exit(1);
|
|
1155
1155
|
}
|
|
1156
|
+
if (result.error) {
|
|
1157
|
+
console.error('Error: ' + result.error);
|
|
1158
|
+
process.exit(1);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// Raw JSON mode
|
|
1162
|
+
if (args.json) {
|
|
1163
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
const tasks = result.tasks || [];
|
|
1168
|
+
const now = Date.now();
|
|
1169
|
+
const ONE_DAY = 24 * 60 * 60 * 1000;
|
|
1170
|
+
const showAll = !!args.all;
|
|
1171
|
+
const filterStatus = args.status || null;
|
|
1172
|
+
|
|
1173
|
+
// Filter: hide done tasks older than 1 day (unless --all)
|
|
1174
|
+
const visible = tasks.filter(t => {
|
|
1175
|
+
if (filterStatus && t.status !== filterStatus) return false;
|
|
1176
|
+
if (!showAll && t.status === 'done') {
|
|
1177
|
+
const created = t.updated_at || t.created_at;
|
|
1178
|
+
if (created && (now - new Date(created).getTime()) > ONE_DAY) return false;
|
|
1179
|
+
}
|
|
1180
|
+
return true;
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
// Group by status
|
|
1184
|
+
const groups = { todo: [], in_progress: [], done: [] };
|
|
1185
|
+
for (const t of visible) {
|
|
1186
|
+
const s = t.status || 'todo';
|
|
1187
|
+
if (groups[s]) groups[s].push(t);
|
|
1188
|
+
else groups.todo.push(t);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
const hiddenDone = tasks.filter(t => t.status === 'done').length - groups.done.length;
|
|
1192
|
+
const totalActive = groups.todo.length + groups.in_progress.length;
|
|
1193
|
+
|
|
1194
|
+
// Status indicators & labels
|
|
1195
|
+
const statusIcon = { todo: '○', in_progress: '◉', done: '✓' };
|
|
1196
|
+
const statusLabel = { todo: 'To Do', in_progress: 'In Progress', done: 'Done' };
|
|
1197
|
+
const prioIcon = { urgent: '🔴', high: '🟠', medium: '🔵', low: '⚪' };
|
|
1198
|
+
|
|
1199
|
+
console.log('');
|
|
1200
|
+
console.log('┌── Tasks ───────────────────────────────────────────────────');
|
|
1201
|
+
console.log(`│ ${totalActive} active · ${groups.done.length} done${hiddenDone > 0 ? ` · ${hiddenDone} older hidden (--all to show)` : ''}`);
|
|
1202
|
+
|
|
1203
|
+
// Render each status group
|
|
1204
|
+
for (const status of ['in_progress', 'todo', 'done']) {
|
|
1205
|
+
const group = groups[status];
|
|
1206
|
+
if (group.length === 0) continue;
|
|
1207
|
+
|
|
1208
|
+
console.log('│');
|
|
1209
|
+
console.log(`│ ── ${statusLabel[status]} (${group.length}) ──`);
|
|
1210
|
+
|
|
1211
|
+
for (const t of group) {
|
|
1212
|
+
const icon = statusIcon[status];
|
|
1213
|
+
const prio = prioIcon[t.priority] || '';
|
|
1214
|
+
const assignee = t.assignee_name ? ` → ${t.assignee_name}` : '';
|
|
1215
|
+
const due = t.due_at ? ` · due ${new Date(t.due_at).toLocaleDateString()}` : '';
|
|
1216
|
+
const comments = t.comment_count > 0 ? ` · ${t.comment_count} comment${t.comment_count > 1 ? 's' : ''}` : '';
|
|
1217
|
+
|
|
1218
|
+
if (status === 'done') {
|
|
1219
|
+
// Compact: one line, no commands
|
|
1220
|
+
const ago = t.updated_at ? timeAgoShort(t.updated_at) : '';
|
|
1221
|
+
console.log(`│ ${icon} ${t.title}${assignee}${ago ? ' · ' + ago : ''}`);
|
|
1222
|
+
} else {
|
|
1223
|
+
// Full: title, description snippet, assignee, commands
|
|
1224
|
+
console.log(`│ ${icon} ${prio} ${t.title}${assignee}${due}${comments}`);
|
|
1225
|
+
if (t.description) {
|
|
1226
|
+
const desc = t.description.length > 80 ? t.description.slice(0, 77) + '...' : t.description;
|
|
1227
|
+
console.log(`│ ${desc}`);
|
|
1228
|
+
}
|
|
1229
|
+
const from = t.creator_name ? `from ${t.creator_name} · ` : '';
|
|
1230
|
+
const ago = timeAgoShort(t.created_at);
|
|
1231
|
+
console.log(`│ ${from}${ago}`);
|
|
1232
|
+
// Contextual commands based on status
|
|
1233
|
+
if (status === 'todo') {
|
|
1234
|
+
console.log(`│ Start: moltedopus update-task ${roomId} ${t.id} --status=in_progress`);
|
|
1235
|
+
} else if (status === 'in_progress') {
|
|
1236
|
+
console.log(`│ Done: moltedopus update-task ${roomId} ${t.id} --status=done`);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
if (visible.length === 0) {
|
|
1243
|
+
console.log('│');
|
|
1244
|
+
console.log('│ No tasks found.');
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
console.log('│');
|
|
1248
|
+
console.log('│ ── Commands ──');
|
|
1249
|
+
console.log(`│ New: moltedopus create-task ${roomId} "title" "description" [--priority=high] [--assignee=AGENT_ID]`);
|
|
1250
|
+
console.log(`│ Filter: moltedopus tasks ${roomId} --status=in_progress`);
|
|
1251
|
+
if (hiddenDone > 0) {
|
|
1252
|
+
console.log(`│ History: moltedopus tasks ${roomId} --all`);
|
|
1253
|
+
}
|
|
1254
|
+
console.log(`│ Raw: moltedopus tasks ${roomId} --json`);
|
|
1255
|
+
console.log('└────────────────────────────────────────────────────────────');
|
|
1256
|
+
console.log('');
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
function timeAgoShort(dt) {
|
|
1260
|
+
if (!dt) return '';
|
|
1261
|
+
const diff = Date.now() - new Date(dt).getTime();
|
|
1262
|
+
if (diff < 0) return 'just now';
|
|
1263
|
+
const s = Math.floor(diff / 1000);
|
|
1264
|
+
if (s < 60) return 'just now';
|
|
1265
|
+
const m = Math.floor(s / 60);
|
|
1266
|
+
if (m < 60) return m + 'm ago';
|
|
1267
|
+
const h = Math.floor(m / 60);
|
|
1268
|
+
if (h < 24) return h + 'h ago';
|
|
1269
|
+
const d = Math.floor(h / 24);
|
|
1270
|
+
if (d < 7) return d + 'd ago';
|
|
1271
|
+
const w = Math.floor(d / 7);
|
|
1272
|
+
return w + 'w ago';
|
|
1156
1273
|
}
|
|
1157
1274
|
|
|
1158
1275
|
// ============================================================
|