clideck 1.29.1 → 1.29.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/handlers.js +11 -4
- package/package.json +1 -1
- package/plugins/autopilot/prompt.md +2 -1
- package/public/index.html +3 -3
- package/public/js/app.js +95 -63
- package/transcript-parser.js +27 -0
package/handlers.js
CHANGED
|
@@ -59,19 +59,26 @@ function getInstalledVersion(bin) {
|
|
|
59
59
|
function checkRemoteUpdate(ws) {
|
|
60
60
|
const now = Date.now();
|
|
61
61
|
if (remoteUpdateCache && now - remoteUpdateCheckedAt < REMOTE_UPDATE_INTERVAL) {
|
|
62
|
-
|
|
62
|
+
ws.send(JSON.stringify({ type: 'remote.update', checked: true, ...remoteUpdateCache }));
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
65
|
const shellOpt = process.platform === 'win32';
|
|
66
66
|
require('child_process').execFile('npm', ['list', '-g', 'clideck-remote', '--json', '--depth=0'], { shell: shellOpt, timeout: 10000 }, (err, stdout) => {
|
|
67
67
|
let installed;
|
|
68
|
-
try { installed = JSON.parse(stdout).dependencies['clideck-remote'].version; }
|
|
68
|
+
try { installed = JSON.parse(stdout).dependencies['clideck-remote'].version; }
|
|
69
|
+
catch {
|
|
70
|
+
ws.send(JSON.stringify({ type: 'remote.update', available: false, checked: false }));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
69
73
|
require('child_process').execFile('npm', ['view', 'clideck-remote', 'version'], { shell: shellOpt, timeout: 10000 }, (err2, stdout2) => {
|
|
70
|
-
if (err2)
|
|
74
|
+
if (err2) {
|
|
75
|
+
ws.send(JSON.stringify({ type: 'remote.update', installed, available: false, checked: false }));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
71
78
|
const latest = stdout2.trim();
|
|
72
79
|
remoteUpdateCache = { installed, latest, available: compareVersions(latest, installed) > 0 };
|
|
73
80
|
remoteUpdateCheckedAt = now;
|
|
74
|
-
|
|
81
|
+
ws.send(JSON.stringify({ type: 'remote.update', checked: true, ...remoteUpdateCache }));
|
|
75
82
|
});
|
|
76
83
|
});
|
|
77
84
|
}
|
package/package.json
CHANGED
|
@@ -51,10 +51,11 @@ RULES
|
|
|
51
51
|
|
|
52
52
|
DO NOT USE notify_user UNLESS ABSOLUTELY NECESSARY
|
|
53
53
|
- Do NOT ask the user if you should continue. Do NOT notify them with requests like "Please resume agent X" or "Should I keep going?" or "Is this a good stopping point?"
|
|
54
|
+
- Do NOT alert the user that some agent asking for the user input before proceeding, this is not an execuse to stop and ask the user what to do. You should route the work to the next best agent until the workflow is truly blocked and cannot proceed without user input. (e.g. if the programmer ask for the user input, first make sure the reivewer or QA agent has not already reviewed the code, if not route it to them first)
|
|
54
55
|
- The user may be away from the computer and expects the agents to keep working until the task is naturally complete.
|
|
55
56
|
- You are autonomous. If you are unsure how to proceed, re-read the workflow state and the latest agent outputs, think differently, and route again.
|
|
56
57
|
- Repeat agents with the same output if needed, unless the routing state shows that the same handoff is being repeated without progress.
|
|
57
|
-
- You steer between agents until the task is complete or the user interrupts you, period.
|
|
58
|
+
- You steer between agents until the task is complete or the user interrupts you, period.
|
|
58
59
|
|
|
59
60
|
HOW TO THINK
|
|
60
61
|
For each decision, reason in this order:
|
package/public/index.html
CHANGED
|
@@ -368,10 +368,10 @@
|
|
|
368
368
|
<!-- Intro (not installed) -->
|
|
369
369
|
<div id="remote-intro" class="hidden px-6 py-6 flex flex-col items-center gap-4">
|
|
370
370
|
<svg class="w-12 h-12 text-slate-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="2" width="14" height="20" rx="2" ry="2"/><line x1="12" y1="18" x2="12.01" y2="18"/></svg>
|
|
371
|
-
<h3 class="text-[13px] font-semibold text-slate-200">CliDeck Mobile Remote</h3>
|
|
372
|
-
<p class="text-xs text-slate-400 text-center leading-relaxed">Control your AI agents from your phone. See live status, send messages, and get notifications — all end-to-end encrypted.</p>
|
|
371
|
+
<h3 id="remote-intro-title" class="text-[13px] font-semibold text-slate-200">CliDeck Mobile Remote</h3>
|
|
372
|
+
<p id="remote-intro-text" class="text-xs text-slate-400 text-center leading-relaxed">Control your AI agents from your phone. See live status, send messages, and get notifications — all end-to-end encrypted.</p>
|
|
373
373
|
<button id="remote-add" class="mt-1 w-full px-4 py-2.5 text-[13px] font-medium bg-blue-600 hover:bg-blue-500 text-white rounded-lg transition-colors">Add to CliDeck</button>
|
|
374
|
-
<p class="text-[11px] text-slate-600 text-center">Installs the <code class="text-slate-500">clideck-remote</code> package via npm</p>
|
|
374
|
+
<p id="remote-intro-foot" class="text-[11px] text-slate-600 text-center">Installs the <code class="text-slate-500">clideck-remote</code> package via npm</p>
|
|
375
375
|
</div>
|
|
376
376
|
|
|
377
377
|
<!-- Installing -->
|
package/public/js/app.js
CHANGED
|
@@ -291,7 +291,11 @@ function connect() {
|
|
|
291
291
|
handleInstallDone(msg.success);
|
|
292
292
|
break;
|
|
293
293
|
case 'remote.update':
|
|
294
|
-
|
|
294
|
+
remoteUpdateInfo = msg?.available ? msg : null;
|
|
295
|
+
if (remotePreflight?.pending) {
|
|
296
|
+
remotePreflight.updateSeen = true;
|
|
297
|
+
finishRemotePreflight();
|
|
298
|
+
}
|
|
295
299
|
break;
|
|
296
300
|
default:
|
|
297
301
|
if (msg.type?.startsWith('plugin.')) dispatchPluginMessage(msg);
|
|
@@ -1046,6 +1050,9 @@ let remoteModalOpen = false;
|
|
|
1046
1050
|
let remoteStatusPoll = null;
|
|
1047
1051
|
let remoteConnectedAt = null;
|
|
1048
1052
|
let remoteStatsTimer = null;
|
|
1053
|
+
let remoteUpdateInfo = null;
|
|
1054
|
+
let remotePreflight = null;
|
|
1055
|
+
let remoteLastStatus = null;
|
|
1049
1056
|
|
|
1050
1057
|
function startRemotePoll() {
|
|
1051
1058
|
stopRemotePoll();
|
|
@@ -1065,6 +1072,66 @@ function setRemotePane(pane) {
|
|
|
1065
1072
|
}
|
|
1066
1073
|
}
|
|
1067
1074
|
|
|
1075
|
+
function showRemoteIntro(opts = {}) {
|
|
1076
|
+
const title = document.getElementById('remote-intro-title');
|
|
1077
|
+
const text = document.getElementById('remote-intro-text');
|
|
1078
|
+
const foot = document.getElementById('remote-intro-foot');
|
|
1079
|
+
const btn = document.getElementById('remote-add');
|
|
1080
|
+
title.textContent = opts.title || 'CliDeck Mobile Remote';
|
|
1081
|
+
text.textContent = opts.text || 'Control your AI agents from your phone. See live status, send messages, and get notifications — all end-to-end encrypted.';
|
|
1082
|
+
foot.innerHTML = opts.foot || 'Installs the <code class="text-slate-500">clideck-remote</code> package via npm';
|
|
1083
|
+
btn.textContent = opts.button || 'Add to CliDeck';
|
|
1084
|
+
setRemotePane('intro');
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
function showRemoteUpdateRequired() {
|
|
1088
|
+
showRemoteIntro({
|
|
1089
|
+
title: 'Update Required',
|
|
1090
|
+
text: `Version ${remoteUpdateInfo.latest} is available. Update CliDeck Remote to continue with mobile pairing on this machine.`,
|
|
1091
|
+
foot: `Installed: <code class="text-slate-500">${esc(remoteUpdateInfo.installed)}</code> · Latest: <code class="text-slate-500">${esc(remoteUpdateInfo.latest)}</code>`,
|
|
1092
|
+
button: 'Update to Continue',
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
function finishRemotePreflight() {
|
|
1097
|
+
if (!remotePreflight?.pending || !remotePreflight.statusSeen || !remotePreflight.updateSeen) return;
|
|
1098
|
+
remotePreflight = null;
|
|
1099
|
+
if (!remoteInstalled) {
|
|
1100
|
+
showRemoteIntro();
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
if (remoteUpdateInfo?.available) {
|
|
1104
|
+
showRemoteUpdateRequired();
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
if (remoteState === 'idle') {
|
|
1108
|
+
remoteState = 'connecting';
|
|
1109
|
+
setRemotePane('connecting');
|
|
1110
|
+
send({ type: 'remote.pair' });
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
if (remoteState === 'paired' && remoteLastStatus?.paired) {
|
|
1114
|
+
setRemotePane('active');
|
|
1115
|
+
setRemoteLock(true);
|
|
1116
|
+
startRemoteStats(remoteLastStatus.pairedAt);
|
|
1117
|
+
const deviceEl = document.getElementById('remote-device-info');
|
|
1118
|
+
if (deviceEl) {
|
|
1119
|
+
const parts = [remoteLastStatus.deviceName, remoteLastStatus.location].filter(Boolean);
|
|
1120
|
+
deviceEl.textContent = parts.length ? parts.join(' · ') : '';
|
|
1121
|
+
}
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
if (remoteState === 'waiting' && remoteLastStatus?.connected && remoteLastStatus?.url) {
|
|
1125
|
+
document.getElementById('remote-url-box').textContent = remoteLastStatus.url;
|
|
1126
|
+
const qrImg = document.getElementById('remote-qr-img');
|
|
1127
|
+
if (remoteLastStatus.qr && remoteLastStatus.qr.startsWith('data:')) { qrImg.src = remoteLastStatus.qr; qrImg.classList.remove('hidden'); }
|
|
1128
|
+
else qrImg.classList.add('hidden');
|
|
1129
|
+
setRemotePane('qr');
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
setRemotePane(remoteState === 'paired' ? 'active' : remoteState === 'waiting' ? 'qr' : 'connecting');
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1068
1135
|
function openRemoteModal() {
|
|
1069
1136
|
remoteModalOpen = true;
|
|
1070
1137
|
remoteModal.classList.remove('hidden');
|
|
@@ -1153,10 +1220,12 @@ function updateRemoteButton() {
|
|
|
1153
1220
|
}
|
|
1154
1221
|
|
|
1155
1222
|
function handleRemoteStatus(msg) {
|
|
1223
|
+
remoteLastStatus = msg;
|
|
1156
1224
|
remoteInstalled = !!msg.installed;
|
|
1157
1225
|
state.remoteVersion = msg.version || (msg.installed ? null : 'not installed');
|
|
1158
1226
|
updateVersionFooter();
|
|
1159
1227
|
const wasPaired = remoteState === 'paired';
|
|
1228
|
+
const preflighting = !!remotePreflight?.pending;
|
|
1160
1229
|
if (!msg.installed) {
|
|
1161
1230
|
remoteState = 'idle';
|
|
1162
1231
|
stopRemotePoll();
|
|
@@ -1165,7 +1234,7 @@ function handleRemoteStatus(msg) {
|
|
|
1165
1234
|
const wasFresh = remoteState !== 'paired';
|
|
1166
1235
|
remoteState = 'paired';
|
|
1167
1236
|
if (!remoteStatusPoll) startRemotePoll();
|
|
1168
|
-
if (wasFresh) {
|
|
1237
|
+
if (wasFresh && !preflighting) {
|
|
1169
1238
|
|
|
1170
1239
|
setRemotePane('active');
|
|
1171
1240
|
setRemoteLock(true);
|
|
@@ -1185,13 +1254,20 @@ function handleRemoteStatus(msg) {
|
|
|
1185
1254
|
if (msg.qr && msg.qr.startsWith('data:')) { qrImg.src = msg.qr; qrImg.classList.remove('hidden'); }
|
|
1186
1255
|
else qrImg.classList.add('hidden');
|
|
1187
1256
|
startRemotePoll();
|
|
1188
|
-
if (remoteModalOpen) setRemotePane('qr');
|
|
1257
|
+
if (!preflighting && remoteModalOpen) setRemotePane('qr');
|
|
1189
1258
|
} else {
|
|
1190
1259
|
remoteState = 'idle';
|
|
1191
1260
|
stopRemotePoll();
|
|
1192
1261
|
if (wasPaired) { stopRemoteStats(); setRemoteLock(false); }
|
|
1193
1262
|
}
|
|
1263
|
+
if (remoteUpdateInfo?.available && remoteModalOpen) {
|
|
1264
|
+
showRemoteUpdateRequired();
|
|
1265
|
+
}
|
|
1194
1266
|
updateRemoteButton();
|
|
1267
|
+
if (remotePreflight?.pending) {
|
|
1268
|
+
remotePreflight.statusSeen = true;
|
|
1269
|
+
finishRemotePreflight();
|
|
1270
|
+
}
|
|
1195
1271
|
}
|
|
1196
1272
|
|
|
1197
1273
|
function handleRemotePaired(msg) {
|
|
@@ -1201,9 +1277,18 @@ function handleRemotePaired(msg) {
|
|
|
1201
1277
|
const qrImg = document.getElementById('remote-qr-img');
|
|
1202
1278
|
if (msg.qr && msg.qr.startsWith('data:')) { qrImg.src = msg.qr; qrImg.classList.remove('hidden'); }
|
|
1203
1279
|
else qrImg.classList.add('hidden');
|
|
1204
|
-
setRemotePane('qr');
|
|
1205
1280
|
updateRemoteButton();
|
|
1206
1281
|
startRemotePoll();
|
|
1282
|
+
if (remoteUpdateInfo?.available && remoteModalOpen) {
|
|
1283
|
+
showRemoteUpdateRequired();
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
if (remotePreflight?.pending) {
|
|
1287
|
+
remotePreflight.statusSeen = true;
|
|
1288
|
+
finishRemotePreflight();
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
setRemotePane('qr');
|
|
1207
1292
|
}
|
|
1208
1293
|
|
|
1209
1294
|
function handleRemoteUnpaired() {
|
|
@@ -1232,6 +1317,7 @@ function appendInstallLog(text) {
|
|
|
1232
1317
|
function handleInstallDone(success) {
|
|
1233
1318
|
if (success) {
|
|
1234
1319
|
remoteInstalled = true;
|
|
1320
|
+
remoteUpdateInfo = null;
|
|
1235
1321
|
// Installed — go straight to pairing
|
|
1236
1322
|
remoteState = 'connecting';
|
|
1237
1323
|
setRemotePane('connecting');
|
|
@@ -1243,74 +1329,20 @@ function handleInstallDone(success) {
|
|
|
1243
1329
|
}
|
|
1244
1330
|
}
|
|
1245
1331
|
|
|
1246
|
-
let remoteUpdateShown = false;
|
|
1247
|
-
|
|
1248
|
-
function showRemoteUpdateToast(msg) {
|
|
1249
|
-
if (remoteUpdateShown) return;
|
|
1250
|
-
remoteUpdateShown = true;
|
|
1251
|
-
|
|
1252
|
-
const toast = document.createElement('div');
|
|
1253
|
-
toast.className = 'fixed bottom-5 right-5 z-[500] w-[360px] bg-slate-800/95 backdrop-blur-sm border border-slate-700/60 rounded-xl shadow-2xl shadow-black/60';
|
|
1254
|
-
toast.style.cssText = 'opacity:0;transform:translateY(12px);transition:opacity 0.3s ease,transform 0.3s ease';
|
|
1255
|
-
|
|
1256
|
-
toast.innerHTML = `
|
|
1257
|
-
<div class="flex items-center gap-2.5 px-4 pt-3.5 pb-1">
|
|
1258
|
-
<svg class="w-5 h-5 flex-shrink-0 text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>
|
|
1259
|
-
<span class="text-[13px] font-semibold text-slate-200">CliDeck Remote Update</span>
|
|
1260
|
-
<button class="dismiss-btn ml-auto w-6 h-6 flex items-center justify-center rounded-md text-slate-500 hover:text-slate-300 hover:bg-slate-700/50 transition-colors">
|
|
1261
|
-
<svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
|
|
1262
|
-
</button>
|
|
1263
|
-
</div>
|
|
1264
|
-
<p class="px-4 pt-1 pb-2.5 text-xs text-slate-400 leading-relaxed">
|
|
1265
|
-
Version <span class="text-slate-300">${esc(msg.latest)}</span> is available (installed: ${esc(msg.installed)}).
|
|
1266
|
-
</p>
|
|
1267
|
-
<div class="px-4 pb-3.5 flex items-center gap-2">
|
|
1268
|
-
<button class="update-btn flex-1 px-3 py-2 text-xs font-medium bg-blue-600 hover:bg-blue-500 text-white rounded-lg transition-colors">Update</button>
|
|
1269
|
-
<button class="dismiss-btn px-3 py-2 text-xs text-slate-500 hover:text-slate-300 transition-colors">Later</button>
|
|
1270
|
-
</div>`;
|
|
1271
|
-
|
|
1272
|
-
const dismiss = () => {
|
|
1273
|
-
toast.style.opacity = '0';
|
|
1274
|
-
toast.style.transform = 'translateY(12px)';
|
|
1275
|
-
setTimeout(() => toast.remove(), 300);
|
|
1276
|
-
};
|
|
1277
|
-
|
|
1278
|
-
toast.querySelectorAll('.dismiss-btn').forEach(b => {
|
|
1279
|
-
b.onclick = () => { dismiss(); setTimeout(() => { remoteUpdateShown = false; }, 600000); };
|
|
1280
|
-
});
|
|
1281
|
-
|
|
1282
|
-
toast.querySelector('.update-btn').onclick = () => {
|
|
1283
|
-
dismiss();
|
|
1284
|
-
remoteUpdateShown = false;
|
|
1285
|
-
document.getElementById('remote-install-log').textContent = '';
|
|
1286
|
-
setRemotePane('installing');
|
|
1287
|
-
openRemoteModal();
|
|
1288
|
-
send({ type: 'remote.install' });
|
|
1289
|
-
};
|
|
1290
|
-
|
|
1291
|
-
document.body.appendChild(toast);
|
|
1292
|
-
requestAnimationFrame(() => { toast.style.opacity = '1'; toast.style.transform = 'translateY(0)'; });
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
1332
|
// Button click
|
|
1296
1333
|
btnRemote.addEventListener('click', () => {
|
|
1297
1334
|
if (remoteModalOpen && remoteState !== 'paired') { closeRemoteModal(); return; }
|
|
1298
1335
|
if (remoteModalOpen) return; // paired — can't dismiss
|
|
1299
1336
|
if (!remoteInstalled) {
|
|
1300
|
-
|
|
1337
|
+
showRemoteIntro();
|
|
1301
1338
|
document.getElementById('remote-install-log').textContent = '';
|
|
1302
1339
|
openRemoteModal();
|
|
1303
1340
|
return;
|
|
1304
1341
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
send({ type: 'remote.pair' });
|
|
1310
|
-
} else {
|
|
1311
|
-
setRemotePane(remoteState === 'paired' ? 'active' : remoteState === 'waiting' ? 'qr' : 'connecting');
|
|
1312
|
-
openRemoteModal();
|
|
1313
|
-
}
|
|
1342
|
+
remotePreflight = { pending: true, statusSeen: false, updateSeen: false };
|
|
1343
|
+
setRemotePane('connecting');
|
|
1344
|
+
openRemoteModal();
|
|
1345
|
+
send({ type: 'remote.status' });
|
|
1314
1346
|
});
|
|
1315
1347
|
|
|
1316
1348
|
// Install button
|
package/transcript-parser.js
CHANGED
|
@@ -4,12 +4,39 @@ function parseTurns(presetId, lines, users) {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
function parseLastAgentOnly(presetId, lines) {
|
|
7
|
+
if (presetId === 'claude-code') return parseLastClaudeAgentOnly(lines);
|
|
7
8
|
const turns = collapseAgentTurns((parsers[presetId] || (() => null))(lines, null));
|
|
8
9
|
if (!turns?.length) return null;
|
|
9
10
|
const last = [...turns].reverse().find(t => t.role === 'agent');
|
|
10
11
|
return last || null;
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
function parseLastClaudeAgentOnly(lines) {
|
|
15
|
+
const userPromptRe = /^(?:[│ ]\s*)?[❯›]\s(.*)$/;
|
|
16
|
+
const agentRe = /^(?:[│ ]\s*)?[⏺•●]\s(.*)$/;
|
|
17
|
+
let promptIdx = -1;
|
|
18
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
19
|
+
if (userPromptRe.test(lines[i])) { promptIdx = i; break; }
|
|
20
|
+
}
|
|
21
|
+
const upperBound = promptIdx >= 0 ? promptIdx : lines.length;
|
|
22
|
+
let start = -1;
|
|
23
|
+
for (let i = upperBound - 1; i >= 0; i--) {
|
|
24
|
+
if (agentRe.test(lines[i])) { start = i; break; }
|
|
25
|
+
}
|
|
26
|
+
if (start < 0) return null;
|
|
27
|
+
const first = lines[start].match(agentRe);
|
|
28
|
+
const turn = { role: 'agent', text: first[1] };
|
|
29
|
+
for (let i = start + 1; i < upperBound; i++) {
|
|
30
|
+
if (userPromptRe.test(lines[i]) || agentRe.test(lines[i])) break;
|
|
31
|
+
let cont = lines[i];
|
|
32
|
+
if (cont.startsWith('│ ')) cont = cont.slice(2);
|
|
33
|
+
else if (cont.startsWith(' ')) cont = cont.slice(2);
|
|
34
|
+
turn.text += '\n' + cont;
|
|
35
|
+
}
|
|
36
|
+
turn.text = turn.text.replace(/\n+$/, '');
|
|
37
|
+
return turn;
|
|
38
|
+
}
|
|
39
|
+
|
|
13
40
|
const parsers = {
|
|
14
41
|
'claude-code': (lines, users) => {
|
|
15
42
|
const known = users?.length ? new Set(users) : null;
|