@masslessai/push-todo 3.7.1 → 3.7.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/api.js +0 -22
- package/lib/cli.js +0 -2
- package/lib/connect.js +5 -51
- package/lib/daemon-health.js +1 -1
- package/lib/daemon.js +67 -16
- package/lib/index.js +0 -1
- package/lib/machine-id.js +1 -2
- package/lib/utils/format.js +10 -0
- package/package.json +1 -1
package/lib/api.js
CHANGED
|
@@ -298,28 +298,6 @@ export async function registerProject(gitRemote, keywords = [], description = ''
|
|
|
298
298
|
return true;
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
/**
|
|
302
|
-
* Validate machine registration.
|
|
303
|
-
*
|
|
304
|
-
* @param {string} machineId - Machine identifier
|
|
305
|
-
* @returns {Promise<Object>} Validation result
|
|
306
|
-
*/
|
|
307
|
-
export async function validateMachine(machineId) {
|
|
308
|
-
const response = await apiRequest('validate-machine', {
|
|
309
|
-
method: 'POST',
|
|
310
|
-
body: JSON.stringify({
|
|
311
|
-
machine_id: machineId
|
|
312
|
-
})
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
if (!response.ok) {
|
|
316
|
-
const text = await response.text();
|
|
317
|
-
throw new Error(`Machine validation failed: ${text}`);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
const data = await response.json();
|
|
321
|
-
return data;
|
|
322
|
-
}
|
|
323
301
|
|
|
324
302
|
/**
|
|
325
303
|
* Get the current CLI version from the server.
|
package/lib/cli.js
CHANGED
|
@@ -89,7 +89,6 @@ ${bold('CONNECT OPTIONS:')}
|
|
|
89
89
|
--check-version Check for updates (JSON output)
|
|
90
90
|
--update Update to latest version
|
|
91
91
|
--validate-key Validate API key (JSON output)
|
|
92
|
-
--validate-machine Validate machine registration (JSON output)
|
|
93
92
|
--validate-project Validate project registration (JSON output)
|
|
94
93
|
--store-e2ee-key <key> Import E2EE encryption key
|
|
95
94
|
--description <text> Project description (with connect)
|
|
@@ -134,7 +133,6 @@ const options = {
|
|
|
134
133
|
'check-version': { type: 'boolean' },
|
|
135
134
|
'update': { type: 'boolean' },
|
|
136
135
|
'validate-key': { type: 'boolean' },
|
|
137
|
-
'validate-machine': { type: 'boolean' },
|
|
138
136
|
'validate-project': { type: 'boolean' },
|
|
139
137
|
'store-e2ee-key': { type: 'string' },
|
|
140
138
|
'description': { type: 'string' }
|
package/lib/connect.js
CHANGED
|
@@ -618,30 +618,6 @@ async function validateApiKeyStatus() {
|
|
|
618
618
|
return { status: 'invalid', message: result.reason };
|
|
619
619
|
}
|
|
620
620
|
|
|
621
|
-
/**
|
|
622
|
-
* Validate machine registration.
|
|
623
|
-
*/
|
|
624
|
-
async function validateMachineStatus() {
|
|
625
|
-
const machineId = getMachineId();
|
|
626
|
-
const machineName = getMachineName();
|
|
627
|
-
|
|
628
|
-
try {
|
|
629
|
-
const result = await api.validateMachine(machineId);
|
|
630
|
-
return {
|
|
631
|
-
status: 'valid',
|
|
632
|
-
machineId,
|
|
633
|
-
machineName,
|
|
634
|
-
...result
|
|
635
|
-
};
|
|
636
|
-
} catch (error) {
|
|
637
|
-
return {
|
|
638
|
-
status: 'error',
|
|
639
|
-
machineId,
|
|
640
|
-
machineName,
|
|
641
|
-
message: error.message
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
621
|
|
|
646
622
|
/**
|
|
647
623
|
* Validate project registration (full validation with warnings).
|
|
@@ -742,11 +718,7 @@ function validateProjectStatus() {
|
|
|
742
718
|
* Get device name for registration.
|
|
743
719
|
*/
|
|
744
720
|
function getDeviceName() {
|
|
745
|
-
|
|
746
|
-
return require('os').hostname() || 'Unknown Device';
|
|
747
|
-
} catch {
|
|
748
|
-
return 'Unknown Device';
|
|
749
|
-
}
|
|
721
|
+
return getMachineName() || 'Unknown Device';
|
|
750
722
|
}
|
|
751
723
|
|
|
752
724
|
/**
|
|
@@ -1090,13 +1062,6 @@ export async function runConnect(options = {}) {
|
|
|
1090
1062
|
return;
|
|
1091
1063
|
}
|
|
1092
1064
|
|
|
1093
|
-
// Handle --validate-machine (JSON output)
|
|
1094
|
-
if (options['validate-machine'] || options.validateMachine) {
|
|
1095
|
-
const result = await validateMachineStatus();
|
|
1096
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1097
|
-
return;
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
1065
|
// Handle --validate-project (JSON output)
|
|
1101
1066
|
if (options['validate-project'] || options.validateProject) {
|
|
1102
1067
|
const result = validateProjectStatus();
|
|
@@ -1191,13 +1156,8 @@ export async function runConnect(options = {}) {
|
|
|
1191
1156
|
}
|
|
1192
1157
|
}
|
|
1193
1158
|
|
|
1194
|
-
//
|
|
1195
|
-
|
|
1196
|
-
if (machineInfo.status === 'valid') {
|
|
1197
|
-
console.log(` Machine: ${machineInfo.machineName}`);
|
|
1198
|
-
} else {
|
|
1199
|
-
console.log(` ⚠️ Machine ID: ${machineInfo.message}`);
|
|
1200
|
-
}
|
|
1159
|
+
// Show machine info
|
|
1160
|
+
console.log(` Machine: ${getMachineName()}`);
|
|
1201
1161
|
console.log(' ' + '='.repeat(40));
|
|
1202
1162
|
console.log('');
|
|
1203
1163
|
|
|
@@ -1274,13 +1234,8 @@ export async function runConnect(options = {}) {
|
|
|
1274
1234
|
}
|
|
1275
1235
|
}
|
|
1276
1236
|
|
|
1277
|
-
//
|
|
1278
|
-
|
|
1279
|
-
if (machineInfo.status === 'valid') {
|
|
1280
|
-
console.log(` Machine: ${machineInfo.machineName}`);
|
|
1281
|
-
} else {
|
|
1282
|
-
console.log(` ⚠️ Machine ID: ${machineInfo.message}`);
|
|
1283
|
-
}
|
|
1237
|
+
// Show machine info
|
|
1238
|
+
console.log(` Machine: ${getMachineName()}`);
|
|
1284
1239
|
console.log(' ' + '='.repeat(40));
|
|
1285
1240
|
console.log('');
|
|
1286
1241
|
console.log(' Your iOS app will sync this automatically.');
|
|
@@ -1297,7 +1252,6 @@ export {
|
|
|
1297
1252
|
checkVersion,
|
|
1298
1253
|
doUpdate,
|
|
1299
1254
|
validateApiKeyStatus,
|
|
1300
|
-
validateMachineStatus,
|
|
1301
1255
|
validateProjectStatus,
|
|
1302
1256
|
validateProjectInfo,
|
|
1303
1257
|
setupE2EE,
|
package/lib/daemon-health.js
CHANGED
|
@@ -197,7 +197,7 @@ function getInstalledVersion() {
|
|
|
197
197
|
* Auto-restarts daemon if version mismatch detected.
|
|
198
198
|
*
|
|
199
199
|
* This prevents stale daemons from running after npm package updates.
|
|
200
|
-
* See: /docs/20260204_daemon_heartbeat_status_indicator_implementation_plan.md
|
|
200
|
+
* See: /docs/20260204_daemon_heartbeat_status_indicator_implementation_plan.md (machine_registry table)
|
|
201
201
|
*/
|
|
202
202
|
export function ensureDaemonRunning() {
|
|
203
203
|
const status = getDaemonStatus();
|
package/lib/daemon.js
CHANGED
|
@@ -297,8 +297,8 @@ async function fetchQueuedTasks() {
|
|
|
297
297
|
const projects = getListedProjects();
|
|
298
298
|
const gitRemotes = Object.keys(projects);
|
|
299
299
|
|
|
300
|
-
// Add
|
|
301
|
-
// See: /docs/20260204_daemon_heartbeat_status_indicator_implementation_plan.md
|
|
300
|
+
// Add machine registry headers for daemon status tracking
|
|
301
|
+
// See: /docs/20260204_daemon_heartbeat_status_indicator_implementation_plan.md (machine_registry table)
|
|
302
302
|
const heartbeatHeaders = {};
|
|
303
303
|
if (machineId && gitRemotes.length > 0) {
|
|
304
304
|
heartbeatHeaders['X-Machine-Id'] = machineId;
|
|
@@ -711,6 +711,43 @@ function extractSessionIdFromStdout(proc, buffer) {
|
|
|
711
711
|
return null;
|
|
712
712
|
}
|
|
713
713
|
|
|
714
|
+
// ==================== Semantic Summary Extraction ====================
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Generate a semantic summary by resuming the Claude session in --print mode.
|
|
718
|
+
* Claude already has full context of what it did — we just ask it to summarize.
|
|
719
|
+
* Uses execFileSync (not execSync) to avoid shell injection.
|
|
720
|
+
*
|
|
721
|
+
* @param {string} worktreePath - Path to the git worktree where Claude ran
|
|
722
|
+
* @param {string} sessionId - Claude session ID
|
|
723
|
+
* @returns {string|null} A short semantic summary, or null if extraction fails
|
|
724
|
+
*/
|
|
725
|
+
function extractSemanticSummary(worktreePath, sessionId) {
|
|
726
|
+
if (!worktreePath || !sessionId) return null;
|
|
727
|
+
|
|
728
|
+
try {
|
|
729
|
+
const result = execFileSync('claude', [
|
|
730
|
+
'--resume', sessionId,
|
|
731
|
+
'--print',
|
|
732
|
+
'Summarize what you accomplished in 1-2 sentences. Be specific about outcomes (what was built, fixed, drafted), not process. If you failed or got stuck, explain what went wrong. If no code changes were made, say so. Do not use markdown.'
|
|
733
|
+
], {
|
|
734
|
+
cwd: worktreePath,
|
|
735
|
+
timeout: 30000,
|
|
736
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
const summary = result.toString().trim();
|
|
740
|
+
if (summary && summary.length > 0) {
|
|
741
|
+
// Cap at 300 chars to keep it concise
|
|
742
|
+
return summary.length > 300 ? summary.slice(0, 297) + '...' : summary;
|
|
743
|
+
}
|
|
744
|
+
return null;
|
|
745
|
+
} catch (error) {
|
|
746
|
+
log(`Semantic summary extraction failed: ${error.message}`);
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
714
751
|
// ==================== Task Execution ====================
|
|
715
752
|
|
|
716
753
|
function updateTaskDetail(displayNumber, updates) {
|
|
@@ -918,24 +955,32 @@ function handleTaskCompletion(displayNumber, exitCode) {
|
|
|
918
955
|
|
|
919
956
|
log(`Task #${displayNumber} completed with code ${exitCode} (${duration}s)`);
|
|
920
957
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
958
|
+
// Extract session ID for both success and failure (needed for AI summary)
|
|
959
|
+
const buffer = taskStdoutBuffer.get(displayNumber) || [];
|
|
960
|
+
const sessionId = extractSessionIdFromStdout(taskInfo.process, buffer);
|
|
961
|
+
const worktreePath = getWorktreePath(displayNumber, projectPath);
|
|
962
|
+
const durationStr = duration < 60 ? `${duration}s` : `${Math.floor(duration / 60)}m ${duration % 60}s`;
|
|
963
|
+
const machineName = getMachineName() || 'Mac';
|
|
925
964
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
965
|
+
if (sessionId) {
|
|
966
|
+
log(`Task #${displayNumber} session_id: ${sessionId}`);
|
|
967
|
+
} else {
|
|
968
|
+
log(`Task #${displayNumber} could not extract session_id`);
|
|
969
|
+
}
|
|
931
970
|
|
|
971
|
+
if (exitCode === 0) {
|
|
932
972
|
// Auto-create PR first so we can include it in the summary
|
|
933
973
|
const prUrl = createPRForTask(displayNumber, summary, projectPath);
|
|
934
974
|
|
|
935
|
-
//
|
|
936
|
-
const
|
|
937
|
-
|
|
938
|
-
|
|
975
|
+
// Ask Claude to summarize what it accomplished
|
|
976
|
+
const semanticSummary = extractSemanticSummary(worktreePath, sessionId);
|
|
977
|
+
|
|
978
|
+
// Combine: semantic summary first (what), then machine metadata (how)
|
|
979
|
+
let executionSummary = '';
|
|
980
|
+
if (semanticSummary) {
|
|
981
|
+
executionSummary = semanticSummary + '\n';
|
|
982
|
+
}
|
|
983
|
+
executionSummary += `Ran for ${durationStr} on ${machineName}.`;
|
|
939
984
|
if (prUrl) {
|
|
940
985
|
executionSummary += ` PR: ${prUrl}`;
|
|
941
986
|
}
|
|
@@ -966,7 +1011,13 @@ function handleTaskCompletion(displayNumber, exitCode) {
|
|
|
966
1011
|
});
|
|
967
1012
|
} else {
|
|
968
1013
|
const stderr = taskInfo.process.stderr?.read()?.toString() || '';
|
|
969
|
-
|
|
1014
|
+
|
|
1015
|
+
// Ask Claude to explain what went wrong (if session exists)
|
|
1016
|
+
const failureSummary = extractSemanticSummary(worktreePath, sessionId);
|
|
1017
|
+
const errorMsg = failureSummary
|
|
1018
|
+
? `${failureSummary}\nExit code ${exitCode}. Ran for ${durationStr} on ${machineName}.`
|
|
1019
|
+
: `Exit code ${exitCode}: ${stderr.slice(0, 200)}`;
|
|
1020
|
+
|
|
970
1021
|
updateTaskStatus(displayNumber, 'failed', { error: errorMsg });
|
|
971
1022
|
|
|
972
1023
|
if (NOTIFY_ON_FAILURE) {
|
package/lib/index.js
CHANGED
package/lib/machine-id.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* File location: ~/.config/push/machine_id
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
12
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from 'fs';
|
|
13
13
|
import { homedir, hostname, platform, release, version, arch } from 'os';
|
|
14
14
|
import { join, dirname } from 'path';
|
|
15
15
|
import { randomUUID } from 'crypto';
|
|
@@ -90,7 +90,6 @@ export function getMachineInfo() {
|
|
|
90
90
|
export function resetMachineId() {
|
|
91
91
|
if (existsSync(MACHINE_ID_FILE)) {
|
|
92
92
|
try {
|
|
93
|
-
const { unlinkSync } = require('fs');
|
|
94
93
|
unlinkSync(MACHINE_ID_FILE);
|
|
95
94
|
} catch {
|
|
96
95
|
// Ignore errors
|
package/lib/utils/format.js
CHANGED
|
@@ -148,6 +148,16 @@ export function formatTaskForDisplay(task) {
|
|
|
148
148
|
lines.push('**Status:** Active');
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
// Show execution summary (semantic + machine metadata)
|
|
152
|
+
const execSummary = task.executionSummary || task.execution_summary;
|
|
153
|
+
if (execSummary) {
|
|
154
|
+
lines.push('');
|
|
155
|
+
lines.push('### What was done');
|
|
156
|
+
for (const line of execSummary.split('\n').filter(Boolean)) {
|
|
157
|
+
lines.push(`> ${line}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
151
161
|
// Show session resume hint for any task with a session ID
|
|
152
162
|
const sessionId = task.executionSessionId || task.execution_session_id;
|
|
153
163
|
if (sessionId && displayNum) {
|