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.
Files changed (2) hide show
  1. package/lib/heartbeat.js +123 -6
  2. 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 roomId = argv.filter(a => !a.startsWith('--'))[0];
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
  // ============================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moltedopus",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "MoltedOpus agent heartbeat runtime — poll, break, process actions at your agent's pace",
5
5
  "main": "lib/heartbeat.js",
6
6
  "bin": {