@masslessai/push-todo 4.2.5 → 4.2.6

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.
@@ -279,6 +279,8 @@ export function checkForAgentUpdate(agentType) {
279
279
 
280
280
  /**
281
281
  * Install a specific version of an agent CLI globally.
282
+ * For claude-code: tries npm first, falls back to `claude update` which
283
+ * handles non-npm installations (brew, app installer, happy-coder wrapper).
282
284
  *
283
285
  * @param {string} agentType
284
286
  * @param {string} targetVersion
@@ -288,6 +290,7 @@ export function performAgentUpdate(agentType, targetVersion) {
288
290
  const agent = AGENTS[agentType];
289
291
  if (!agent?.npmPackage) return false;
290
292
 
293
+ // Try npm install first (works for standard npm global installs)
291
294
  try {
292
295
  execFileSync('npm', ['install', '-g', `${agent.npmPackage}@${targetVersion}`], {
293
296
  timeout: 120000,
@@ -295,8 +298,29 @@ export function performAgentUpdate(agentType, targetVersion) {
295
298
  });
296
299
  return true;
297
300
  } catch {
298
- return false;
301
+ // npm failed — fall through to agent-specific fallbacks
299
302
  }
303
+
304
+ // Fallback: use the agent's own update command (handles non-npm installs)
305
+ if (agentType === 'claude-code') {
306
+ try {
307
+ const env = { ...process.env };
308
+ delete env.CLAUDECODE;
309
+ delete env.CLAUDE_CODE_ENTRYPOINT;
310
+ execFileSync('claude', ['update'], {
311
+ timeout: 120000,
312
+ stdio: 'pipe',
313
+ env,
314
+ });
315
+ // Verify the update actually worked
316
+ const after = detectAgentVersion('claude-code');
317
+ return after.installed && after.version != null;
318
+ } catch {
319
+ return false;
320
+ }
321
+ }
322
+
323
+ return false;
300
324
  }
301
325
 
302
326
  /**
@@ -333,6 +357,51 @@ export function checkAllAgentUpdates({ force = false } = {}) {
333
357
  return results;
334
358
  }
335
359
 
360
+ /**
361
+ * Pre-flight check: verify an agent meets minimum version before task execution.
362
+ * If below minimum, attempts an immediate update and rechecks.
363
+ *
364
+ * @param {string} agentType
365
+ * @returns {{ ok: boolean, version: string|null, error: string|null }}
366
+ */
367
+ export function ensureAgentReady(agentType) {
368
+ const agent = AGENTS[agentType];
369
+ if (!agent) {
370
+ return { ok: false, version: null, error: `Unknown agent type: ${agentType}` };
371
+ }
372
+
373
+ const info = detectAgentVersion(agentType);
374
+ if (!info.installed) {
375
+ return { ok: false, version: null, error: `${agentType} CLI not found` };
376
+ }
377
+ if (!info.version) {
378
+ return { ok: false, version: null, error: `${agentType} installed but version unknown` };
379
+ }
380
+
381
+ // Check minimum version
382
+ if (agent.minVersion && compareSemver(info.version, agent.minVersion) < 0) {
383
+ // Attempt immediate update
384
+ const latest = fetchLatestAgentVersion(agentType);
385
+ if (latest?.version) {
386
+ const updated = performAgentUpdate(agentType, latest.version);
387
+ if (updated) {
388
+ // Recheck after update
389
+ const after = detectAgentVersion(agentType);
390
+ if (after.version && compareSemver(after.version, agent.minVersion) >= 0) {
391
+ return { ok: true, version: after.version, error: null };
392
+ }
393
+ }
394
+ }
395
+ return {
396
+ ok: false,
397
+ version: info.version,
398
+ error: `${agentType} v${info.version} is below minimum v${agent.minVersion} (needs --worktree support). Update with: claude update`,
399
+ };
400
+ }
401
+
402
+ return { ok: true, version: info.version, error: null };
403
+ }
404
+
336
405
  // ==================== Version Parity ====================
337
406
 
338
407
  /**
package/lib/daemon.js CHANGED
@@ -25,7 +25,7 @@ import { getProjectContext, buildSmartPrompt, invalidateCache } from './context-
25
25
  import { sendMacNotification } from './utils/notify.js';
26
26
  import { checkAndRunDueJobs } from './cron.js';
27
27
  import { runHeartbeatChecks } from './heartbeat.js';
28
- import { getAgentVersions, formatAgentVersionSummary, checkAllAgentUpdates, performAgentUpdate, checkVersionParity } from './agent-versions.js';
28
+ import { getAgentVersions, formatAgentVersionSummary, checkAllAgentUpdates, performAgentUpdate, checkVersionParity, ensureAgentReady } from './agent-versions.js';
29
29
  import { checkAllProjectsFreshness } from './project-freshness.js';
30
30
  import { getStatus as getLaunchAgentStatus, install as refreshLaunchAgent } from './launchagent.js';
31
31
 
@@ -2261,6 +2261,17 @@ async function executeTask(task) {
2261
2261
  log(`Task #${displayNumber}: Project ${gitRemote} -> ${projectPath}`);
2262
2262
  }
2263
2263
 
2264
+ // Pre-flight: verify agent CLI meets minimum version (attempts auto-update if not)
2265
+ const agentType = taskActionType || 'claude-code';
2266
+ const readiness = ensureAgentReady(agentType);
2267
+ if (!readiness.ok) {
2268
+ logError(`Task #${displayNumber}: ${readiness.error}`);
2269
+ await updateTaskStatus(displayNumber, 'failed', {
2270
+ error: readiness.error
2271
+ }, taskId);
2272
+ return null;
2273
+ }
2274
+
2264
2275
  // Pre-assign session ID so we can store it at claim time (not rely on parsing stdout)
2265
2276
  const preAssignedSessionId = randomUUID();
2266
2277
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@masslessai/push-todo",
3
- "version": "4.2.5",
3
+ "version": "4.2.6",
4
4
  "description": "Voice tasks from Push iOS app for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {