@masslessai/push-todo 3.6.3 → 3.6.5

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 (3) hide show
  1. package/SKILL.md +85 -2
  2. package/lib/daemon.js +9 -110
  3. package/package.json +1 -1
package/SKILL.md CHANGED
@@ -36,13 +36,23 @@ When this command is invoked:
36
36
 
37
37
  5. Ask which task the user wants to work on
38
38
 
39
- 6. **Check for resumable daemon sessions first:**
39
+ 6. **Check if the daemon is currently working on this task:**
40
+ - If the task output shows `**Status:** 🔄 Running`:
41
+ - The daemon is actively working on this task RIGHT NOW
42
+ - Follow the [Live Session Status](#live-session-status) procedure to show progress
43
+ - Do NOT start working on this task — the daemon is already on it
44
+ - If the task output shows `**Status:** ⚡ Queued for Mac execution`:
45
+ - The task is queued and waiting for the daemon to pick it up
46
+ - Tell the user: "This task is queued and will be picked up by the daemon shortly."
47
+ - Do NOT start working on this task
48
+
49
+ 7. **Check for resumable daemon sessions:**
40
50
  - If the task output contains `**Session:** Resumable`, the daemon already ran Claude Code on this task
41
51
  - Do NOT start working from scratch — automatically load the daemon's session context
42
52
  - Follow the [Auto-Resume from Session Transcript](#auto-resume-from-session-transcript) procedure below
43
53
  - Only if the session transcript cannot be found should you begin working from scratch
44
54
 
45
- 7. If no resumable session exists, begin working on the task normally
55
+ 8. If no resumable session exists, begin working on the task normally
46
56
 
47
57
  ## Review Mode
48
58
 
@@ -272,6 +282,79 @@ push-todo resume <number>
272
282
  ```
273
283
  This launches a full interactive Claude Code session with the daemon's complete conversation history.
274
284
 
285
+ ## Live Session Status
286
+
287
+ When a task is currently running (daemon is actively working on it), read the live session transcript to show the user what's happening.
288
+
289
+ ### Step 1: Locate the Live Session File
290
+
291
+ The daemon runs Claude in a git worktree. Find the active session:
292
+
293
+ ```bash
294
+ # Get machine ID suffix for worktree name
295
+ MACHINE_ID=$(cat ~/.config/push/machine_id 2>/dev/null)
296
+ SUFFIX=$(echo "$MACHINE_ID" | rev | cut -d'-' -f1 | rev | cut -c1-8)
297
+ TASK_NUM=<display_number>
298
+
299
+ # Session files are stored under ~/.claude/projects/ with path-encoded directory names
300
+ SESSION_DIR="$HOME/.claude/projects/-Users-$(whoami)-projects-push-${TASK_NUM}-${SUFFIX}"
301
+
302
+ # Find the most recent .jsonl file (the active session)
303
+ ls -t "${SESSION_DIR}"/*.jsonl 2>/dev/null | head -1
304
+ ```
305
+
306
+ ### Step 2: Extract Recent Activity
307
+
308
+ Read the last portion of the JSONL transcript to see what Claude is currently doing:
309
+
310
+ ```bash
311
+ tail -100 "<session_file>" | node -e "
312
+ const lines = [];
313
+ process.stdin.on('data', d => lines.push(d));
314
+ process.stdin.on('end', () => {
315
+ const entries = Buffer.concat(lines).toString().split('\n')
316
+ .filter(Boolean).map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
317
+
318
+ const assistantMsgs = entries.filter(e => e.type === 'assistant');
319
+ const edits = [];
320
+ const reads = [];
321
+ const texts = [];
322
+
323
+ assistantMsgs.forEach(a => {
324
+ (a.message?.content || []).forEach(b => {
325
+ if (b.type === 'text' && b.text.trim()) texts.push(b.text.trim());
326
+ if (b.type === 'tool_use') {
327
+ if (b.name === 'Edit' || b.name === 'Write') edits.push(b.input?.file_path);
328
+ if (b.name === 'Read') reads.push(b.input?.file_path);
329
+ }
330
+ });
331
+ });
332
+
333
+ console.log('FILES_READ:', JSON.stringify([...new Set(reads)].slice(-10)));
334
+ console.log('FILES_EDITED:', JSON.stringify([...new Set(edits)]));
335
+ console.log('---RECENT_ACTIVITY---');
336
+ texts.slice(-5).forEach(t => console.log(t.slice(0, 200)));
337
+ console.log('---END---');
338
+ });
339
+ "
340
+ ```
341
+
342
+ ### Step 3: Present Status to User
343
+
344
+ Show a concise summary:
345
+ 1. "The daemon is currently working on this task"
346
+ 2. Files it has read so far
347
+ 3. Files it has edited so far
348
+ 4. Its most recent reasoning/activity (last few text messages)
349
+ 5. "Check back in a few minutes, or run `/push-todo <number>` again for an update"
350
+
351
+ Do NOT offer to start working on the task — the daemon is already handling it.
352
+
353
+ ### Fallback
354
+
355
+ If the session file cannot be found (daemon just started, no output yet):
356
+ - Tell the user: "The daemon just started working on this task. Run `/push-todo <number>` again in a minute for a progress update."
357
+
275
358
  ## CLI Reference
276
359
 
277
360
  The `push-todo` CLI supports these commands:
package/lib/daemon.js CHANGED
@@ -8,12 +8,7 @@
8
8
  * Architecture:
9
9
  * - Git branch = worktree = Claude session (1:1:1 mapping)
10
10
  * - Uses Claude's --continue to resume sessions in worktrees
11
- * - Certainty analysis determines execution mode (immediate, planning, or clarify)
12
- *
13
- * Certainty-Based Execution:
14
- * - High certainty (>= 0.7): Execute immediately in standard mode
15
- * - Medium certainty (0.4-0.7): Execute with --plan flag (planning mode first)
16
- * - Low certainty (< 0.4): Update todo with clarification questions, skip execution
11
+ * - All tasks execute with bypassPermissions mode
17
12
  *
18
13
  * Ported from: plugins/push-todo/scripts/daemon.py
19
14
  */
@@ -238,57 +233,6 @@ function decryptTaskFields(task) {
238
233
  return decrypted;
239
234
  }
240
235
 
241
- // ==================== Certainty Analysis ====================
242
-
243
- let CertaintyAnalyzer = null;
244
- let getExecutionMode = null;
245
-
246
- try {
247
- const certainty = await import('./certainty.js');
248
- CertaintyAnalyzer = certainty.CertaintyAnalyzer;
249
- getExecutionMode = certainty.getExecutionMode;
250
- } catch {
251
- // Certainty analysis not available
252
- }
253
-
254
- function analyzeTaskCertainty(task) {
255
- if (!CertaintyAnalyzer) {
256
- log('Certainty analysis not available, executing directly');
257
- return null;
258
- }
259
-
260
- const content = task.normalizedContent || task.normalized_content ||
261
- task.content || task.summary || '';
262
- const summary = task.summary;
263
- const transcript = task.originalTranscript || task.original_transcript;
264
-
265
- try {
266
- const analyzer = new CertaintyAnalyzer();
267
- const analysis = analyzer.analyze(content, summary, transcript);
268
-
269
- const displayNum = task.displayNumber || task.display_number;
270
- log(`Task #${displayNum} certainty: ${analysis.score} (${analysis.level})`);
271
-
272
- if (analysis.reasons && analysis.reasons.length > 0) {
273
- for (const reason of analysis.reasons.slice(0, 3)) {
274
- log(` - ${reason.factor}: ${reason.explanation}`);
275
- }
276
- }
277
-
278
- return analysis;
279
- } catch (e) {
280
- log(`Certainty analysis failed: ${e.message}`);
281
- return null;
282
- }
283
- }
284
-
285
- function determineExecutionMode(analysis) {
286
- if (!analysis || !getExecutionMode) {
287
- return 'immediate';
288
- }
289
- return getExecutionMode(analysis);
290
- }
291
-
292
236
  // ==================== API ====================
293
237
 
294
238
  function isRetryableError(error) {
@@ -813,25 +757,13 @@ function executeTask(task) {
813
757
  taskId: task.id || task.todo_id || '',
814
758
  summary,
815
759
  status: 'running',
816
- phase: 'analyzing',
817
- detail: 'Analyzing task certainty...',
760
+ phase: 'starting',
761
+ detail: 'Starting Claude...',
818
762
  startedAt: Date.now(),
819
763
  gitRemote
820
764
  });
821
765
 
822
- log(`Analyzing task #${displayNumber}: ${content.slice(0, 60)}...`);
823
-
824
- // Analyze certainty
825
- const analysis = analyzeTaskCertainty(task);
826
- let executionMode = determineExecutionMode(analysis);
827
-
828
- log(`Task #${displayNumber} execution mode: ${executionMode}`);
829
-
830
- // Low-certainty tasks: run in planning mode instead of blocking
831
- if (executionMode === 'clarify') {
832
- log(`Task #${displayNumber} low certainty - running in planning mode instead of blocking`);
833
- executionMode = 'planning';
834
- }
766
+ log(`Executing task #${displayNumber}: ${content.slice(0, 60)}...`);
835
767
 
836
768
  // Create worktree
837
769
  const worktreePath = createWorktree(displayNumber, projectPath);
@@ -844,38 +776,16 @@ function executeTask(task) {
844
776
  taskProjectPaths.set(displayNumber, projectPath);
845
777
 
846
778
  // Build prompt
847
- let prompt;
848
- if (executionMode === 'planning') {
849
- const reasonsText = analysis?.reasons?.slice(0, 3).map(r => `- ${r.explanation}`).join('\n') || '';
850
- prompt = `Work on Push task #${displayNumber}:
851
-
852
- ${content}
853
-
854
- IMPORTANT: This task has medium certainty (score: ${analysis?.score ?? 'N/A'}).
855
- Please START BY ENTERING PLAN MODE to clarify the approach before implementing.
856
-
857
- Reasons for lower certainty:
858
- ${reasonsText}
859
-
860
- After your plan is approved, implement the changes.
861
-
862
- When you're done, the SessionEnd hook will automatically report completion to Supabase.
863
-
864
- If you need to understand the codebase, start by reading the CLAUDE.md file if it exists.`;
865
- } else {
866
- prompt = `Work on Push task #${displayNumber}:
779
+ const prompt = `Work on Push task #${displayNumber}:
867
780
 
868
781
  ${content}
869
782
 
870
783
  IMPORTANT: When you're done, the SessionEnd hook will automatically report completion to Supabase.
871
784
 
872
785
  If you need to understand the codebase, start by reading the CLAUDE.md file if it exists.`;
873
- }
874
786
 
875
787
  // Update status to running
876
- updateTaskStatus(displayNumber, 'running', {
877
- certaintyScore: analysis?.score
878
- });
788
+ updateTaskStatus(displayNumber, 'running');
879
789
 
880
790
  // Build Claude command
881
791
  const allowedTools = [
@@ -909,8 +819,7 @@ If you need to understand the codebase, start by reading the CLAUDE.md file if i
909
819
  task,
910
820
  displayNumber,
911
821
  startTime: Date.now(),
912
- projectPath,
913
- executionMode
822
+ projectPath
914
823
  };
915
824
 
916
825
  runningTasks.set(displayNumber, taskInfo);
@@ -953,22 +862,13 @@ If you need to understand the codebase, start by reading the CLAUDE.md file if i
953
862
  updateStatusFile();
954
863
  });
955
864
 
956
- const modeDesc = executionMode === 'planning' ? 'planning mode' : 'standard mode';
957
865
  updateTaskDetail(displayNumber, {
958
866
  phase: 'executing',
959
- detail: `Running Claude in ${modeDesc}...`,
867
+ detail: 'Running Claude...',
960
868
  claudePid: child.pid
961
869
  });
962
870
 
963
- log(`Started Claude for task #${displayNumber} in ${modeDesc} (PID: ${child.pid})`);
964
-
965
- if (executionMode === 'planning') {
966
- sendMacNotification(
967
- `Task #${displayNumber} started (planning)`,
968
- `${summary.slice(0, 60)}...`,
969
- 'default'
970
- );
971
- }
871
+ log(`Started Claude for task #${displayNumber} (PID: ${child.pid})`);
972
872
 
973
873
  return taskInfo;
974
874
  } catch (error) {
@@ -1258,7 +1158,6 @@ async function mainLoop() {
1258
1158
  log(`Polling interval: ${POLL_INTERVAL / 1000}s`);
1259
1159
  log(`Max concurrent tasks: ${MAX_CONCURRENT_TASKS}`);
1260
1160
  log(`E2EE: ${e2eeAvailable ? 'Available' : 'Not available'}`);
1261
- log(`Certainty analysis: ${CertaintyAnalyzer ? 'Available' : 'Not available'}`);
1262
1161
  log(`Log file: ${LOG_FILE}`);
1263
1162
 
1264
1163
  // Show registered projects
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@masslessai/push-todo",
3
- "version": "3.6.3",
3
+ "version": "3.6.5",
4
4
  "description": "Voice tasks from Push iOS app for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {