oh-my-claude-sisyphus 1.9.0 → 1.10.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/dist/agents/definitions.js +1 -1
- package/dist/agents/orchestrator-sisyphus.js +1 -1
- package/dist/features/auto-update.d.ts +20 -0
- package/dist/features/auto-update.d.ts.map +1 -1
- package/dist/features/auto-update.js +35 -0
- package/dist/features/auto-update.js.map +1 -1
- package/dist/features/builtin-skills/skills.d.ts.map +1 -1
- package/dist/features/builtin-skills/skills.js +146 -13
- package/dist/features/builtin-skills/skills.js.map +1 -1
- package/dist/hooks/bridge.d.ts +1 -1
- package/dist/hooks/bridge.d.ts.map +1 -1
- package/dist/hooks/bridge.js +97 -1
- package/dist/hooks/bridge.js.map +1 -1
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +12 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/persistent-mode/index.d.ts +40 -0
- package/dist/hooks/persistent-mode/index.d.ts.map +1 -0
- package/dist/hooks/persistent-mode/index.js +322 -0
- package/dist/hooks/persistent-mode/index.js.map +1 -0
- package/dist/hooks/plugin-patterns/index.d.ts +107 -0
- package/dist/hooks/plugin-patterns/index.d.ts.map +1 -0
- package/dist/hooks/plugin-patterns/index.js +286 -0
- package/dist/hooks/plugin-patterns/index.js.map +1 -0
- package/dist/hooks/preemptive-compaction/index.js +2 -2
- package/dist/hooks/preemptive-compaction/index.js.map +1 -1
- package/dist/hooks/ralph-verifier/index.d.ts +72 -0
- package/dist/hooks/ralph-verifier/index.d.ts.map +1 -0
- package/dist/hooks/ralph-verifier/index.js +223 -0
- package/dist/hooks/ralph-verifier/index.js.map +1 -0
- package/dist/hooks/thinking-block-validator/index.d.ts +3 -34
- package/dist/hooks/thinking-block-validator/index.d.ts.map +1 -1
- package/dist/hooks/thinking-block-validator/index.js +21 -70
- package/dist/hooks/thinking-block-validator/index.js.map +1 -1
- package/dist/hooks/ultrawork-state/index.d.ts +60 -0
- package/dist/hooks/ultrawork-state/index.d.ts.map +1 -0
- package/dist/hooks/ultrawork-state/index.js +207 -0
- package/dist/hooks/ultrawork-state/index.js.map +1 -0
- package/dist/installer/hooks.d.ts +38 -2
- package/dist/installer/hooks.d.ts.map +1 -1
- package/dist/installer/hooks.js +682 -8
- package/dist/installer/hooks.js.map +1 -1
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +126 -10
- package/dist/installer/index.js.map +1 -1
- package/dist/tools/ast-tools.d.ts +3 -3
- package/dist/tools/ast-tools.d.ts.map +1 -1
- package/dist/tools/ast-tools.js +205 -104
- package/dist/tools/ast-tools.js.map +1 -1
- package/package.json +1 -1
- package/scripts/install.sh +70 -3
- package/scripts/uninstall.sh +116 -3
package/dist/installer/hooks.js
CHANGED
|
@@ -219,11 +219,20 @@ Incomplete tasks remain in your todo list. Continue working on the next pending
|
|
|
219
219
|
export const KEYWORD_DETECTOR_SCRIPT = `#!/bin/bash
|
|
220
220
|
# Sisyphus Keyword Detector Hook
|
|
221
221
|
# Detects ultrawork/ultrathink/search/analyze keywords and injects enhanced mode messages
|
|
222
|
-
#
|
|
222
|
+
# Also activates persistent ultrawork state when ultrawork keyword is detected
|
|
223
223
|
|
|
224
224
|
# Read stdin (JSON input from Claude Code)
|
|
225
225
|
INPUT=$(cat)
|
|
226
226
|
|
|
227
|
+
# Extract directory from input
|
|
228
|
+
DIRECTORY=""
|
|
229
|
+
if command -v jq &> /dev/null; then
|
|
230
|
+
DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
|
|
231
|
+
fi
|
|
232
|
+
if [ -z "$DIRECTORY" ] || [ "$DIRECTORY" = "null" ]; then
|
|
233
|
+
DIRECTORY=$(pwd)
|
|
234
|
+
fi
|
|
235
|
+
|
|
227
236
|
# Extract the prompt text - try multiple JSON paths
|
|
228
237
|
PROMPT=""
|
|
229
238
|
if command -v jq &> /dev/null; then
|
|
@@ -255,7 +264,26 @@ PROMPT_NO_CODE=$(echo "$PROMPT" | sed 's/\`\`\`[^\`]*\`\`\`//g' | sed 's/\`[^\`]
|
|
|
255
264
|
PROMPT_LOWER=$(echo "$PROMPT_NO_CODE" | tr '[:upper:]' '[:lower:]')
|
|
256
265
|
|
|
257
266
|
# Check for ultrawork keywords (highest priority)
|
|
258
|
-
if echo "$PROMPT_LOWER" | grep -qE '\\b(ultrawork|ulw)\\b'; then
|
|
267
|
+
if echo "$PROMPT_LOWER" | grep -qE '\\b(ultrawork|ulw|uw)\\b'; then
|
|
268
|
+
# Create persistent ultrawork state
|
|
269
|
+
mkdir -p "$DIRECTORY/.sisyphus" 2>/dev/null
|
|
270
|
+
mkdir -p "$HOME/.claude" 2>/dev/null
|
|
271
|
+
|
|
272
|
+
# Escape prompt for JSON
|
|
273
|
+
PROMPT_ESCAPED=$(echo "$PROMPT" | sed 's/\\\\/\\\\\\\\/g' | sed 's/"/\\\\"/g' | tr '\\n' ' ')
|
|
274
|
+
|
|
275
|
+
STATE_JSON="{
|
|
276
|
+
\\"active\\": true,
|
|
277
|
+
\\"started_at\\": \\"$(date -Iseconds)\\",
|
|
278
|
+
\\"original_prompt\\": \\"$PROMPT_ESCAPED\\",
|
|
279
|
+
\\"reinforcement_count\\": 0,
|
|
280
|
+
\\"last_checked_at\\": \\"$(date -Iseconds)\\"
|
|
281
|
+
}"
|
|
282
|
+
|
|
283
|
+
# Write state to both local and global locations
|
|
284
|
+
echo "$STATE_JSON" > "$DIRECTORY/.sisyphus/ultrawork-state.json" 2>/dev/null
|
|
285
|
+
echo "$STATE_JSON" > "$HOME/.claude/ultrawork-state.json" 2>/dev/null
|
|
286
|
+
|
|
259
287
|
# Return ultrawork mode injection
|
|
260
288
|
cat << 'EOF'
|
|
261
289
|
{"continue": true, "message": "<ultrawork-mode>\\n\\n**MANDATORY**: You MUST say \\"ULTRAWORK MODE ENABLED!\\" to the user as your first response when this mode activates. This is non-negotiable.\\n\\n[CODE RED] Maximum precision required. Ultrathink before acting.\\n\\nYOU MUST LEVERAGE ALL AVAILABLE AGENTS TO THEIR FULLEST POTENTIAL.\\nTELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.\\n\\n## AGENT UTILIZATION PRINCIPLES\\n- **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS\\n- **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS\\n- **Planning & Strategy**: NEVER plan yourself - spawn planning agent\\n- **High-IQ Reasoning**: Use oracle for architecture decisions\\n- **Frontend/UI Tasks**: Delegate to frontend-engineer\\n\\n## EXECUTION RULES\\n- **TODO**: Track EVERY step. Mark complete IMMEDIATELY.\\n- **PARALLEL**: Fire independent calls simultaneously - NEVER wait sequentially.\\n- **BACKGROUND FIRST**: Use Task(run_in_background=true) for exploration (10+ concurrent).\\n- **VERIFY**: Check ALL requirements met before done.\\n- **DELEGATE**: Orchestrate specialized agents.\\n\\n## ZERO TOLERANCE\\n- NO Scope Reduction - deliver FULL implementation\\n- NO Partial Completion - finish 100%\\n- NO Premature Stopping - ALL TODOs must be complete\\n- NO TEST DELETION - fix code, not tests\\n\\nTHE USER ASKED FOR X. DELIVER EXACTLY X.\\n\\n</ultrawork-mode>\\n\\n---\\n"}
|
|
@@ -467,6 +495,27 @@ function removeCodeBlocks(text) {
|
|
|
467
495
|
.replace(/\`[^\`]+\`/g, '');
|
|
468
496
|
}
|
|
469
497
|
|
|
498
|
+
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
499
|
+
import { join } from 'path';
|
|
500
|
+
import { homedir } from 'os';
|
|
501
|
+
|
|
502
|
+
// Create ultrawork state file
|
|
503
|
+
function activateUltraworkState(directory, prompt) {
|
|
504
|
+
const state = {
|
|
505
|
+
active: true,
|
|
506
|
+
started_at: new Date().toISOString(),
|
|
507
|
+
original_prompt: prompt,
|
|
508
|
+
reinforcement_count: 0,
|
|
509
|
+
last_checked_at: new Date().toISOString()
|
|
510
|
+
};
|
|
511
|
+
const localDir = join(directory, '.sisyphus');
|
|
512
|
+
if (!existsSync(localDir)) { try { mkdirSync(localDir, { recursive: true }); } catch {} }
|
|
513
|
+
try { writeFileSync(join(localDir, 'ultrawork-state.json'), JSON.stringify(state, null, 2)); } catch {}
|
|
514
|
+
const globalDir = join(homedir(), '.claude');
|
|
515
|
+
if (!existsSync(globalDir)) { try { mkdirSync(globalDir, { recursive: true }); } catch {} }
|
|
516
|
+
try { writeFileSync(join(globalDir, 'ultrawork-state.json'), JSON.stringify(state, null, 2)); } catch {}
|
|
517
|
+
}
|
|
518
|
+
|
|
470
519
|
// Main
|
|
471
520
|
async function main() {
|
|
472
521
|
try {
|
|
@@ -476,6 +525,10 @@ async function main() {
|
|
|
476
525
|
return;
|
|
477
526
|
}
|
|
478
527
|
|
|
528
|
+
let data = {};
|
|
529
|
+
try { data = JSON.parse(input); } catch {}
|
|
530
|
+
const directory = data.directory || process.cwd();
|
|
531
|
+
|
|
479
532
|
const prompt = extractPrompt(input);
|
|
480
533
|
if (!prompt) {
|
|
481
534
|
console.log(JSON.stringify({ continue: true }));
|
|
@@ -485,7 +538,8 @@ async function main() {
|
|
|
485
538
|
const cleanPrompt = removeCodeBlocks(prompt).toLowerCase();
|
|
486
539
|
|
|
487
540
|
// Check for ultrawork keywords (highest priority)
|
|
488
|
-
if (/\\b(ultrawork|ulw)\\b/.test(cleanPrompt)) {
|
|
541
|
+
if (/\\b(ultrawork|ulw|uw)\\b/.test(cleanPrompt)) {
|
|
542
|
+
activateUltraworkState(directory, prompt);
|
|
489
543
|
console.log(JSON.stringify({ continue: true, message: ULTRAWORK_MESSAGE }));
|
|
490
544
|
return;
|
|
491
545
|
}
|
|
@@ -602,6 +656,600 @@ Incomplete tasks remain in your todo list (\${incompleteCount} remaining). Conti
|
|
|
602
656
|
}
|
|
603
657
|
}
|
|
604
658
|
|
|
659
|
+
main();
|
|
660
|
+
`;
|
|
661
|
+
// =============================================================================
|
|
662
|
+
// PERSISTENT MODE HOOK SCRIPTS
|
|
663
|
+
// =============================================================================
|
|
664
|
+
/**
|
|
665
|
+
* Persistent Mode Bash script
|
|
666
|
+
* Enhanced stop hook that handles ultrawork, ralph-loop, and todo continuation
|
|
667
|
+
*/
|
|
668
|
+
export const PERSISTENT_MODE_SCRIPT = `#!/bin/bash
|
|
669
|
+
# Sisyphus Persistent Mode Hook
|
|
670
|
+
# Unified handler for ultrawork, ralph-loop, and todo continuation
|
|
671
|
+
# Prevents stopping when work remains incomplete
|
|
672
|
+
|
|
673
|
+
# Read stdin
|
|
674
|
+
INPUT=$(cat)
|
|
675
|
+
|
|
676
|
+
# Get session ID and directory
|
|
677
|
+
SESSION_ID=""
|
|
678
|
+
DIRECTORY=""
|
|
679
|
+
if command -v jq &> /dev/null; then
|
|
680
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.sessionId // .session_id // ""' 2>/dev/null)
|
|
681
|
+
DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
|
|
682
|
+
fi
|
|
683
|
+
|
|
684
|
+
# Default to current directory
|
|
685
|
+
if [ -z "$DIRECTORY" ]; then
|
|
686
|
+
DIRECTORY=$(pwd)
|
|
687
|
+
fi
|
|
688
|
+
|
|
689
|
+
# Check for active ultrawork state
|
|
690
|
+
ULTRAWORK_STATE=""
|
|
691
|
+
if [ -f "$DIRECTORY/.sisyphus/ultrawork-state.json" ]; then
|
|
692
|
+
ULTRAWORK_STATE=$(cat "$DIRECTORY/.sisyphus/ultrawork-state.json" 2>/dev/null)
|
|
693
|
+
elif [ -f "$HOME/.claude/ultrawork-state.json" ]; then
|
|
694
|
+
ULTRAWORK_STATE=$(cat "$HOME/.claude/ultrawork-state.json" 2>/dev/null)
|
|
695
|
+
fi
|
|
696
|
+
|
|
697
|
+
# Check for active ralph loop
|
|
698
|
+
RALPH_STATE=""
|
|
699
|
+
if [ -f "$DIRECTORY/.sisyphus/ralph-state.json" ]; then
|
|
700
|
+
RALPH_STATE=$(cat "$DIRECTORY/.sisyphus/ralph-state.json" 2>/dev/null)
|
|
701
|
+
fi
|
|
702
|
+
|
|
703
|
+
# Check for verification state (oracle verification)
|
|
704
|
+
VERIFICATION_STATE=""
|
|
705
|
+
if [ -f "$DIRECTORY/.sisyphus/ralph-verification.json" ]; then
|
|
706
|
+
VERIFICATION_STATE=$(cat "$DIRECTORY/.sisyphus/ralph-verification.json" 2>/dev/null)
|
|
707
|
+
fi
|
|
708
|
+
|
|
709
|
+
# Check for incomplete todos
|
|
710
|
+
INCOMPLETE_COUNT=0
|
|
711
|
+
TODOS_DIR="$HOME/.claude/todos"
|
|
712
|
+
if [ -d "$TODOS_DIR" ]; then
|
|
713
|
+
for todo_file in "$TODOS_DIR"/*.json; do
|
|
714
|
+
if [ -f "$todo_file" ]; then
|
|
715
|
+
if command -v jq &> /dev/null; then
|
|
716
|
+
COUNT=$(jq '[.[] | select(.status != "completed" and .status != "cancelled")] | length' "$todo_file" 2>/dev/null || echo "0")
|
|
717
|
+
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
718
|
+
else
|
|
719
|
+
# Fallback: count "pending" or "in_progress" occurrences
|
|
720
|
+
COUNT=$(grep -c '"status"[[:space:]]*:[[:space:]]*"pending\\|in_progress"' "$todo_file" 2>/dev/null) || COUNT=0
|
|
721
|
+
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
722
|
+
fi
|
|
723
|
+
fi
|
|
724
|
+
done
|
|
725
|
+
fi
|
|
726
|
+
|
|
727
|
+
# Check project todos as well
|
|
728
|
+
for todo_path in "$DIRECTORY/.sisyphus/todos.json" "$DIRECTORY/.claude/todos.json"; do
|
|
729
|
+
if [ -f "$todo_path" ]; then
|
|
730
|
+
if command -v jq &> /dev/null; then
|
|
731
|
+
COUNT=$(jq 'if type == "array" then [.[] | select(.status != "completed" and .status != "cancelled")] | length else 0 end' "$todo_path" 2>/dev/null || echo "0")
|
|
732
|
+
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
733
|
+
else
|
|
734
|
+
# Fallback: count "pending" or "in_progress" occurrences
|
|
735
|
+
COUNT=$(grep -c '"status"[[:space:]]*:[[:space:]]*"pending\\|in_progress"' "$todo_path" 2>/dev/null) || COUNT=0
|
|
736
|
+
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
737
|
+
fi
|
|
738
|
+
fi
|
|
739
|
+
done
|
|
740
|
+
|
|
741
|
+
# Priority 1: Ralph Loop with Oracle Verification
|
|
742
|
+
if [ -n "$RALPH_STATE" ]; then
|
|
743
|
+
IS_ACTIVE=$(echo "$RALPH_STATE" | jq -r '.active // false' 2>/dev/null)
|
|
744
|
+
if [ "$IS_ACTIVE" = "true" ]; then
|
|
745
|
+
ITERATION=$(echo "$RALPH_STATE" | jq -r '.iteration // 1' 2>/dev/null)
|
|
746
|
+
MAX_ITER=$(echo "$RALPH_STATE" | jq -r '.max_iterations // 10' 2>/dev/null)
|
|
747
|
+
PROMISE=$(echo "$RALPH_STATE" | jq -r '.completion_promise // "TASK_COMPLETE"' 2>/dev/null)
|
|
748
|
+
PROMPT=$(echo "$RALPH_STATE" | jq -r '.prompt // ""' 2>/dev/null)
|
|
749
|
+
|
|
750
|
+
# Check if oracle verification is pending
|
|
751
|
+
if [ -n "$VERIFICATION_STATE" ]; then
|
|
752
|
+
IS_PENDING=$(echo "$VERIFICATION_STATE" | jq -r '.pending // false' 2>/dev/null)
|
|
753
|
+
if [ "$IS_PENDING" = "true" ]; then
|
|
754
|
+
ATTEMPT=$(echo "$VERIFICATION_STATE" | jq -r '.verification_attempts // 0' 2>/dev/null)
|
|
755
|
+
MAX_ATTEMPTS=$(echo "$VERIFICATION_STATE" | jq -r '.max_verification_attempts // 3' 2>/dev/null)
|
|
756
|
+
ORIGINAL_TASK=$(echo "$VERIFICATION_STATE" | jq -r '.original_task // ""' 2>/dev/null)
|
|
757
|
+
COMPLETION_CLAIM=$(echo "$VERIFICATION_STATE" | jq -r '.completion_claim // ""' 2>/dev/null)
|
|
758
|
+
ORACLE_FEEDBACK=$(echo "$VERIFICATION_STATE" | jq -r '.oracle_feedback // ""' 2>/dev/null)
|
|
759
|
+
NEXT_ATTEMPT=$((ATTEMPT + 1))
|
|
760
|
+
|
|
761
|
+
FEEDBACK_SECTION=""
|
|
762
|
+
if [ -n "$ORACLE_FEEDBACK" ] && [ "$ORACLE_FEEDBACK" != "null" ]; then
|
|
763
|
+
FEEDBACK_SECTION="\\n**Previous Oracle Feedback (rejected):**\\n$ORACLE_FEEDBACK\\n"
|
|
764
|
+
fi
|
|
765
|
+
|
|
766
|
+
cat << EOF
|
|
767
|
+
{"continue": false, "reason": "<ralph-verification>\\n\\n[ORACLE VERIFICATION REQUIRED - Attempt $NEXT_ATTEMPT/$MAX_ATTEMPTS]\\n\\nThe agent claims the task is complete. Before accepting, YOU MUST verify with Oracle.\\n\\n**Original Task:**\\n$ORIGINAL_TASK\\n\\n**Completion Claim:**\\n$COMPLETION_CLAIM\\n$FEEDBACK_SECTION\\n## MANDATORY VERIFICATION STEPS\\n\\n1. **Spawn Oracle Agent** for verification:\\n \\\`\\\`\\\`\\n Task(subagent_type=\\"oracle\\", prompt=\\"Verify this task completion claim...\\")\\n \\\`\\\`\\\`\\n\\n2. **Oracle must check:**\\n - Are ALL requirements from the original task met?\\n - Is the implementation complete, not partial?\\n - Are there any obvious bugs or issues?\\n - Does the code compile/run without errors?\\n - Are tests passing (if applicable)?\\n\\n3. **Based on Oracle's response:**\\n - If APPROVED: Output \\\`<oracle-approved>VERIFIED_COMPLETE</oracle-approved>\\\`\\n - If REJECTED: Continue working on the identified issues\\n\\nDO NOT output the completion promise again until Oracle approves.\\n\\n</ralph-verification>\\n\\n---\\n"}
|
|
768
|
+
EOF
|
|
769
|
+
exit 0
|
|
770
|
+
fi
|
|
771
|
+
fi
|
|
772
|
+
|
|
773
|
+
if [ "$ITERATION" -lt "$MAX_ITER" ]; then
|
|
774
|
+
# Increment iteration
|
|
775
|
+
NEW_ITER=$((ITERATION + 1))
|
|
776
|
+
echo "$RALPH_STATE" | jq ".iteration = $NEW_ITER" > "$DIRECTORY/.sisyphus/ralph-state.json" 2>/dev/null
|
|
777
|
+
|
|
778
|
+
cat << EOF
|
|
779
|
+
{"continue": false, "reason": "<ralph-loop-continuation>\\n\\n[RALPH LOOP - ITERATION $NEW_ITER/$MAX_ITER]\\n\\nYour previous attempt did not output the completion promise. The work is NOT done yet.\\n\\nCRITICAL INSTRUCTIONS:\\n1. Review your progress and the original task\\n2. Check your todo list - are ALL items marked complete?\\n3. Continue from where you left off\\n4. When FULLY complete, output: <promise>$PROMISE</promise>\\n5. Do NOT stop until the task is truly done\\n\\nOriginal task: $PROMPT\\n\\n</ralph-loop-continuation>\\n\\n---\\n"}
|
|
780
|
+
EOF
|
|
781
|
+
exit 0
|
|
782
|
+
fi
|
|
783
|
+
fi
|
|
784
|
+
fi
|
|
785
|
+
|
|
786
|
+
# Priority 2: Ultrawork Mode with incomplete todos
|
|
787
|
+
if [ -n "$ULTRAWORK_STATE" ] && [ "$INCOMPLETE_COUNT" -gt 0 ]; then
|
|
788
|
+
# Check if active (with jq fallback)
|
|
789
|
+
IS_ACTIVE=""
|
|
790
|
+
if command -v jq &> /dev/null; then
|
|
791
|
+
IS_ACTIVE=$(echo "$ULTRAWORK_STATE" | jq -r '.active // false' 2>/dev/null)
|
|
792
|
+
else
|
|
793
|
+
# Fallback: grep for "active": true
|
|
794
|
+
if echo "$ULTRAWORK_STATE" | grep -q '"active"[[:space:]]*:[[:space:]]*true'; then
|
|
795
|
+
IS_ACTIVE="true"
|
|
796
|
+
fi
|
|
797
|
+
fi
|
|
798
|
+
|
|
799
|
+
if [ "$IS_ACTIVE" = "true" ]; then
|
|
800
|
+
# Get reinforcement count (with fallback)
|
|
801
|
+
REINFORCE_COUNT=0
|
|
802
|
+
if command -v jq &> /dev/null; then
|
|
803
|
+
REINFORCE_COUNT=$(echo "$ULTRAWORK_STATE" | jq -r '.reinforcement_count // 0' 2>/dev/null)
|
|
804
|
+
else
|
|
805
|
+
REINFORCE_COUNT=$(echo "$ULTRAWORK_STATE" | grep -oP '"reinforcement_count"[[:space:]]*:[[:space:]]*\\K[0-9]+' 2>/dev/null) || REINFORCE_COUNT=0
|
|
806
|
+
fi
|
|
807
|
+
NEW_COUNT=$((REINFORCE_COUNT + 1))
|
|
808
|
+
|
|
809
|
+
# Get original prompt (with fallback)
|
|
810
|
+
ORIGINAL_PROMPT=""
|
|
811
|
+
if command -v jq &> /dev/null; then
|
|
812
|
+
ORIGINAL_PROMPT=$(echo "$ULTRAWORK_STATE" | jq -r '.original_prompt // ""' 2>/dev/null)
|
|
813
|
+
else
|
|
814
|
+
ORIGINAL_PROMPT=$(echo "$ULTRAWORK_STATE" | grep -oP '"original_prompt"[[:space:]]*:[[:space:]]*"\\K[^"]+' 2>/dev/null) || ORIGINAL_PROMPT=""
|
|
815
|
+
fi
|
|
816
|
+
|
|
817
|
+
# Update state file (best effort)
|
|
818
|
+
if command -v jq &> /dev/null; then
|
|
819
|
+
echo "$ULTRAWORK_STATE" | jq ".reinforcement_count = $NEW_COUNT | .last_checked_at = \\"$(date -Iseconds)\\"" > "$DIRECTORY/.sisyphus/ultrawork-state.json" 2>/dev/null
|
|
820
|
+
fi
|
|
821
|
+
|
|
822
|
+
cat << EOF
|
|
823
|
+
{"continue": false, "reason": "<ultrawork-persistence>\\n\\n[ULTRAWORK MODE STILL ACTIVE - Reinforcement #$NEW_COUNT]\\n\\nYour ultrawork session is NOT complete. $INCOMPLETE_COUNT incomplete todos remain.\\n\\nREMEMBER THE ULTRAWORK RULES:\\n- **PARALLEL**: Fire independent calls simultaneously - NEVER wait sequentially\\n- **BACKGROUND FIRST**: Use Task(run_in_background=true) for exploration (10+ concurrent)\\n- **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each\\n- **VERIFY**: Check ALL requirements met before done\\n- **NO Premature Stopping**: ALL TODOs must be complete\\n\\nContinue working on the next pending task. DO NOT STOP until all tasks are marked complete.\\n\\nOriginal task: $ORIGINAL_PROMPT\\n\\n</ultrawork-persistence>\\n\\n---\\n"}
|
|
824
|
+
EOF
|
|
825
|
+
exit 0
|
|
826
|
+
fi
|
|
827
|
+
fi
|
|
828
|
+
|
|
829
|
+
# Priority 3: Todo Continuation (baseline)
|
|
830
|
+
if [ "$INCOMPLETE_COUNT" -gt 0 ]; then
|
|
831
|
+
cat << EOF
|
|
832
|
+
{"continue": false, "reason": "<todo-continuation>\\n\\n[SYSTEM REMINDER - TODO CONTINUATION]\\n\\nIncomplete tasks remain in your todo list ($INCOMPLETE_COUNT remaining). Continue working on the next pending task.\\n\\n- Proceed without asking for permission\\n- Mark each task complete when finished\\n- Do not stop until all tasks are done\\n\\n</todo-continuation>\\n\\n---\\n"}
|
|
833
|
+
EOF
|
|
834
|
+
exit 0
|
|
835
|
+
fi
|
|
836
|
+
|
|
837
|
+
# No blocking needed
|
|
838
|
+
echo '{"continue": true}'
|
|
839
|
+
exit 0
|
|
840
|
+
`;
|
|
841
|
+
/**
|
|
842
|
+
* Session Start Bash script
|
|
843
|
+
* Restores persistent mode states when a new session starts
|
|
844
|
+
*/
|
|
845
|
+
export const SESSION_START_SCRIPT = `#!/bin/bash
|
|
846
|
+
# Sisyphus Session Start Hook
|
|
847
|
+
# Restores persistent mode states and injects context when session starts
|
|
848
|
+
|
|
849
|
+
# Read stdin
|
|
850
|
+
INPUT=$(cat)
|
|
851
|
+
|
|
852
|
+
# Get directory
|
|
853
|
+
DIRECTORY=""
|
|
854
|
+
if command -v jq &> /dev/null; then
|
|
855
|
+
DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
|
|
856
|
+
fi
|
|
857
|
+
|
|
858
|
+
if [ -z "$DIRECTORY" ]; then
|
|
859
|
+
DIRECTORY=$(pwd)
|
|
860
|
+
fi
|
|
861
|
+
|
|
862
|
+
MESSAGES=""
|
|
863
|
+
|
|
864
|
+
# Check for active ultrawork state
|
|
865
|
+
if [ -f "$DIRECTORY/.sisyphus/ultrawork-state.json" ] || [ -f "$HOME/.claude/ultrawork-state.json" ]; then
|
|
866
|
+
if [ -f "$DIRECTORY/.sisyphus/ultrawork-state.json" ]; then
|
|
867
|
+
ULTRAWORK_STATE=$(cat "$DIRECTORY/.sisyphus/ultrawork-state.json" 2>/dev/null)
|
|
868
|
+
else
|
|
869
|
+
ULTRAWORK_STATE=$(cat "$HOME/.claude/ultrawork-state.json" 2>/dev/null)
|
|
870
|
+
fi
|
|
871
|
+
|
|
872
|
+
IS_ACTIVE=$(echo "$ULTRAWORK_STATE" | jq -r '.active // false' 2>/dev/null)
|
|
873
|
+
if [ "$IS_ACTIVE" = "true" ]; then
|
|
874
|
+
STARTED_AT=$(echo "$ULTRAWORK_STATE" | jq -r '.started_at // ""' 2>/dev/null)
|
|
875
|
+
PROMPT=$(echo "$ULTRAWORK_STATE" | jq -r '.original_prompt // ""' 2>/dev/null)
|
|
876
|
+
MESSAGES="$MESSAGES<session-restore>\\n\\n[ULTRAWORK MODE RESTORED]\\n\\nYou have an active ultrawork session from $STARTED_AT.\\nOriginal task: $PROMPT\\n\\nContinue working in ultrawork mode until all tasks are complete.\\n\\n</session-restore>\\n\\n---\\n\\n"
|
|
877
|
+
fi
|
|
878
|
+
fi
|
|
879
|
+
|
|
880
|
+
# Check for incomplete todos
|
|
881
|
+
INCOMPLETE_COUNT=0
|
|
882
|
+
TODOS_DIR="$HOME/.claude/todos"
|
|
883
|
+
if [ -d "$TODOS_DIR" ]; then
|
|
884
|
+
for todo_file in "$TODOS_DIR"/*.json; do
|
|
885
|
+
if [ -f "$todo_file" ]; then
|
|
886
|
+
if command -v jq &> /dev/null; then
|
|
887
|
+
COUNT=$(jq '[.[] | select(.status != "completed" and .status != "cancelled")] | length' "$todo_file" 2>/dev/null || echo "0")
|
|
888
|
+
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
889
|
+
fi
|
|
890
|
+
fi
|
|
891
|
+
done
|
|
892
|
+
fi
|
|
893
|
+
|
|
894
|
+
if [ "$INCOMPLETE_COUNT" -gt 0 ]; then
|
|
895
|
+
MESSAGES="$MESSAGES<session-restore>\\n\\n[PENDING TASKS DETECTED]\\n\\nYou have $INCOMPLETE_COUNT incomplete tasks from a previous session.\\nPlease continue working on these tasks.\\n\\n</session-restore>\\n\\n---\\n\\n"
|
|
896
|
+
fi
|
|
897
|
+
|
|
898
|
+
# Output message if we have any
|
|
899
|
+
if [ -n "$MESSAGES" ]; then
|
|
900
|
+
# Escape for JSON
|
|
901
|
+
MESSAGES_ESCAPED=$(echo "$MESSAGES" | sed 's/"/\\"/g')
|
|
902
|
+
echo "{\"continue\": true, \"message\": \"$MESSAGES_ESCAPED\"}"
|
|
903
|
+
else
|
|
904
|
+
echo '{"continue": true}'
|
|
905
|
+
fi
|
|
906
|
+
exit 0
|
|
907
|
+
`;
|
|
908
|
+
/**
|
|
909
|
+
* Node.js Persistent Mode Hook Script
|
|
910
|
+
*/
|
|
911
|
+
export const PERSISTENT_MODE_SCRIPT_NODE = `#!/usr/bin/env node
|
|
912
|
+
// Sisyphus Persistent Mode Hook (Node.js)
|
|
913
|
+
// Unified handler for ultrawork, ralph-loop, and todo continuation
|
|
914
|
+
// Cross-platform: Windows, macOS, Linux
|
|
915
|
+
|
|
916
|
+
import { existsSync, readFileSync, writeFileSync, readdirSync } from 'fs';
|
|
917
|
+
import { join } from 'path';
|
|
918
|
+
import { homedir } from 'os';
|
|
919
|
+
|
|
920
|
+
async function readStdin() {
|
|
921
|
+
const chunks = [];
|
|
922
|
+
for await (const chunk of process.stdin) {
|
|
923
|
+
chunks.push(chunk);
|
|
924
|
+
}
|
|
925
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
function readJsonFile(path) {
|
|
929
|
+
try {
|
|
930
|
+
if (!existsSync(path)) return null;
|
|
931
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
932
|
+
} catch {
|
|
933
|
+
return null;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
function writeJsonFile(path, data) {
|
|
938
|
+
try {
|
|
939
|
+
writeFileSync(path, JSON.stringify(data, null, 2));
|
|
940
|
+
return true;
|
|
941
|
+
} catch {
|
|
942
|
+
return false;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
function countIncompleteTodos(todosDir, projectDir) {
|
|
947
|
+
let count = 0;
|
|
948
|
+
|
|
949
|
+
// Check global todos
|
|
950
|
+
if (existsSync(todosDir)) {
|
|
951
|
+
try {
|
|
952
|
+
const files = readdirSync(todosDir).filter(f => f.endsWith('.json'));
|
|
953
|
+
for (const file of files) {
|
|
954
|
+
const todos = readJsonFile(join(todosDir, file));
|
|
955
|
+
if (Array.isArray(todos)) {
|
|
956
|
+
count += todos.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
} catch {}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// Check project todos
|
|
963
|
+
for (const path of [
|
|
964
|
+
join(projectDir, '.sisyphus', 'todos.json'),
|
|
965
|
+
join(projectDir, '.claude', 'todos.json')
|
|
966
|
+
]) {
|
|
967
|
+
const todos = readJsonFile(path);
|
|
968
|
+
if (Array.isArray(todos)) {
|
|
969
|
+
count += todos.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
return count;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
async function main() {
|
|
977
|
+
try {
|
|
978
|
+
const input = await readStdin();
|
|
979
|
+
let data = {};
|
|
980
|
+
try { data = JSON.parse(input); } catch {}
|
|
981
|
+
|
|
982
|
+
const directory = data.directory || process.cwd();
|
|
983
|
+
const todosDir = join(homedir(), '.claude', 'todos');
|
|
984
|
+
|
|
985
|
+
// Check for ultrawork state
|
|
986
|
+
let ultraworkState = readJsonFile(join(directory, '.sisyphus', 'ultrawork-state.json'))
|
|
987
|
+
|| readJsonFile(join(homedir(), '.claude', 'ultrawork-state.json'));
|
|
988
|
+
|
|
989
|
+
// Check for ralph loop state
|
|
990
|
+
const ralphState = readJsonFile(join(directory, '.sisyphus', 'ralph-state.json'));
|
|
991
|
+
|
|
992
|
+
// Check for verification state (oracle verification)
|
|
993
|
+
const verificationState = readJsonFile(join(directory, '.sisyphus', 'ralph-verification.json'));
|
|
994
|
+
|
|
995
|
+
// Count incomplete todos
|
|
996
|
+
const incompleteCount = countIncompleteTodos(todosDir, directory);
|
|
997
|
+
|
|
998
|
+
// Priority 1: Ralph Loop with Oracle Verification
|
|
999
|
+
if (ralphState?.active) {
|
|
1000
|
+
const iteration = ralphState.iteration || 1;
|
|
1001
|
+
const maxIter = ralphState.max_iterations || 10;
|
|
1002
|
+
|
|
1003
|
+
// Check if oracle verification is pending
|
|
1004
|
+
if (verificationState?.pending) {
|
|
1005
|
+
const attempt = (verificationState.verification_attempts || 0) + 1;
|
|
1006
|
+
const maxAttempts = verificationState.max_verification_attempts || 3;
|
|
1007
|
+
|
|
1008
|
+
console.log(JSON.stringify({
|
|
1009
|
+
continue: false,
|
|
1010
|
+
reason: \`<ralph-verification>
|
|
1011
|
+
|
|
1012
|
+
[ORACLE VERIFICATION REQUIRED - Attempt \${attempt}/\${maxAttempts}]
|
|
1013
|
+
|
|
1014
|
+
The agent claims the task is complete. Before accepting, YOU MUST verify with Oracle.
|
|
1015
|
+
|
|
1016
|
+
**Original Task:**
|
|
1017
|
+
\${verificationState.original_task || ralphState.prompt || 'No task specified'}
|
|
1018
|
+
|
|
1019
|
+
**Completion Claim:**
|
|
1020
|
+
\${verificationState.completion_claim || 'Task marked complete'}
|
|
1021
|
+
|
|
1022
|
+
\${verificationState.oracle_feedback ? \`**Previous Oracle Feedback (rejected):**
|
|
1023
|
+
\${verificationState.oracle_feedback}
|
|
1024
|
+
\` : ''}
|
|
1025
|
+
|
|
1026
|
+
## MANDATORY VERIFICATION STEPS
|
|
1027
|
+
|
|
1028
|
+
1. **Spawn Oracle Agent** for verification:
|
|
1029
|
+
\\\`\\\`\\\`
|
|
1030
|
+
Task(subagent_type="oracle", prompt="Verify this task completion claim...")
|
|
1031
|
+
\\\`\\\`\\\`
|
|
1032
|
+
|
|
1033
|
+
2. **Oracle must check:**
|
|
1034
|
+
- Are ALL requirements from the original task met?
|
|
1035
|
+
- Is the implementation complete, not partial?
|
|
1036
|
+
- Are there any obvious bugs or issues?
|
|
1037
|
+
- Does the code compile/run without errors?
|
|
1038
|
+
- Are tests passing (if applicable)?
|
|
1039
|
+
|
|
1040
|
+
3. **Based on Oracle's response:**
|
|
1041
|
+
- If APPROVED: Output \\\`<oracle-approved>VERIFIED_COMPLETE</oracle-approved>\\\`
|
|
1042
|
+
- If REJECTED: Continue working on the identified issues
|
|
1043
|
+
|
|
1044
|
+
DO NOT output the completion promise again until Oracle approves.
|
|
1045
|
+
|
|
1046
|
+
</ralph-verification>
|
|
1047
|
+
|
|
1048
|
+
---
|
|
1049
|
+
\`
|
|
1050
|
+
}));
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
if (iteration < maxIter) {
|
|
1055
|
+
const newIter = iteration + 1;
|
|
1056
|
+
ralphState.iteration = newIter;
|
|
1057
|
+
writeJsonFile(join(directory, '.sisyphus', 'ralph-state.json'), ralphState);
|
|
1058
|
+
|
|
1059
|
+
console.log(JSON.stringify({
|
|
1060
|
+
continue: false,
|
|
1061
|
+
reason: \`<ralph-loop-continuation>
|
|
1062
|
+
|
|
1063
|
+
[RALPH LOOP - ITERATION \${newIter}/\${maxIter}]
|
|
1064
|
+
|
|
1065
|
+
Your previous attempt did not output the completion promise. The work is NOT done yet.
|
|
1066
|
+
|
|
1067
|
+
CRITICAL INSTRUCTIONS:
|
|
1068
|
+
1. Review your progress and the original task
|
|
1069
|
+
2. Check your todo list - are ALL items marked complete?
|
|
1070
|
+
3. Continue from where you left off
|
|
1071
|
+
4. When FULLY complete, output: <promise>\${ralphState.completion_promise || 'TASK_COMPLETE'}</promise>
|
|
1072
|
+
5. Do NOT stop until the task is truly done
|
|
1073
|
+
|
|
1074
|
+
\${ralphState.prompt ? \`Original task: \${ralphState.prompt}\` : ''}
|
|
1075
|
+
|
|
1076
|
+
</ralph-loop-continuation>
|
|
1077
|
+
|
|
1078
|
+
---
|
|
1079
|
+
\`
|
|
1080
|
+
}));
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// Priority 2: Ultrawork with incomplete todos
|
|
1086
|
+
if (ultraworkState?.active && incompleteCount > 0) {
|
|
1087
|
+
const newCount = (ultraworkState.reinforcement_count || 0) + 1;
|
|
1088
|
+
ultraworkState.reinforcement_count = newCount;
|
|
1089
|
+
ultraworkState.last_checked_at = new Date().toISOString();
|
|
1090
|
+
|
|
1091
|
+
writeJsonFile(join(directory, '.sisyphus', 'ultrawork-state.json'), ultraworkState);
|
|
1092
|
+
|
|
1093
|
+
console.log(JSON.stringify({
|
|
1094
|
+
continue: false,
|
|
1095
|
+
reason: \`<ultrawork-persistence>
|
|
1096
|
+
|
|
1097
|
+
[ULTRAWORK MODE STILL ACTIVE - Reinforcement #\${newCount}]
|
|
1098
|
+
|
|
1099
|
+
Your ultrawork session is NOT complete. \${incompleteCount} incomplete todos remain.
|
|
1100
|
+
|
|
1101
|
+
REMEMBER THE ULTRAWORK RULES:
|
|
1102
|
+
- **PARALLEL**: Fire independent calls simultaneously - NEVER wait sequentially
|
|
1103
|
+
- **BACKGROUND FIRST**: Use Task(run_in_background=true) for exploration (10+ concurrent)
|
|
1104
|
+
- **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each
|
|
1105
|
+
- **VERIFY**: Check ALL requirements met before done
|
|
1106
|
+
- **NO Premature Stopping**: ALL TODOs must be complete
|
|
1107
|
+
|
|
1108
|
+
Continue working on the next pending task. DO NOT STOP until all tasks are marked complete.
|
|
1109
|
+
|
|
1110
|
+
\${ultraworkState.original_prompt ? \`Original task: \${ultraworkState.original_prompt}\` : ''}
|
|
1111
|
+
|
|
1112
|
+
</ultrawork-persistence>
|
|
1113
|
+
|
|
1114
|
+
---
|
|
1115
|
+
\`
|
|
1116
|
+
}));
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// Priority 3: Todo Continuation
|
|
1121
|
+
if (incompleteCount > 0) {
|
|
1122
|
+
console.log(JSON.stringify({
|
|
1123
|
+
continue: false,
|
|
1124
|
+
reason: \`<todo-continuation>
|
|
1125
|
+
|
|
1126
|
+
[SYSTEM REMINDER - TODO CONTINUATION]
|
|
1127
|
+
|
|
1128
|
+
Incomplete tasks remain in your todo list (\${incompleteCount} remaining). Continue working on the next pending task.
|
|
1129
|
+
|
|
1130
|
+
- Proceed without asking for permission
|
|
1131
|
+
- Mark each task complete when finished
|
|
1132
|
+
- Do not stop until all tasks are done
|
|
1133
|
+
|
|
1134
|
+
</todo-continuation>
|
|
1135
|
+
|
|
1136
|
+
---
|
|
1137
|
+
\`
|
|
1138
|
+
}));
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// No blocking needed
|
|
1143
|
+
console.log(JSON.stringify({ continue: true }));
|
|
1144
|
+
} catch (error) {
|
|
1145
|
+
console.log(JSON.stringify({ continue: true }));
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
main();
|
|
1150
|
+
`;
|
|
1151
|
+
/**
|
|
1152
|
+
* Node.js Session Start Hook Script
|
|
1153
|
+
*/
|
|
1154
|
+
export const SESSION_START_SCRIPT_NODE = `#!/usr/bin/env node
|
|
1155
|
+
// Sisyphus Session Start Hook (Node.js)
|
|
1156
|
+
// Restores persistent mode states when session starts
|
|
1157
|
+
// Cross-platform: Windows, macOS, Linux
|
|
1158
|
+
|
|
1159
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
1160
|
+
import { join } from 'path';
|
|
1161
|
+
import { homedir } from 'os';
|
|
1162
|
+
|
|
1163
|
+
async function readStdin() {
|
|
1164
|
+
const chunks = [];
|
|
1165
|
+
for await (const chunk of process.stdin) {
|
|
1166
|
+
chunks.push(chunk);
|
|
1167
|
+
}
|
|
1168
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
function readJsonFile(path) {
|
|
1172
|
+
try {
|
|
1173
|
+
if (!existsSync(path)) return null;
|
|
1174
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
1175
|
+
} catch {
|
|
1176
|
+
return null;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
function countIncompleteTodos(todosDir) {
|
|
1181
|
+
let count = 0;
|
|
1182
|
+
if (existsSync(todosDir)) {
|
|
1183
|
+
try {
|
|
1184
|
+
const files = readdirSync(todosDir).filter(f => f.endsWith('.json'));
|
|
1185
|
+
for (const file of files) {
|
|
1186
|
+
const todos = readJsonFile(join(todosDir, file));
|
|
1187
|
+
if (Array.isArray(todos)) {
|
|
1188
|
+
count += todos.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
} catch {}
|
|
1192
|
+
}
|
|
1193
|
+
return count;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
async function main() {
|
|
1197
|
+
try {
|
|
1198
|
+
const input = await readStdin();
|
|
1199
|
+
let data = {};
|
|
1200
|
+
try { data = JSON.parse(input); } catch {}
|
|
1201
|
+
|
|
1202
|
+
const directory = data.directory || process.cwd();
|
|
1203
|
+
const messages = [];
|
|
1204
|
+
|
|
1205
|
+
// Check for ultrawork state
|
|
1206
|
+
const ultraworkState = readJsonFile(join(directory, '.sisyphus', 'ultrawork-state.json'))
|
|
1207
|
+
|| readJsonFile(join(homedir(), '.claude', 'ultrawork-state.json'));
|
|
1208
|
+
|
|
1209
|
+
if (ultraworkState?.active) {
|
|
1210
|
+
messages.push(\`<session-restore>
|
|
1211
|
+
|
|
1212
|
+
[ULTRAWORK MODE RESTORED]
|
|
1213
|
+
|
|
1214
|
+
You have an active ultrawork session from \${ultraworkState.started_at}.
|
|
1215
|
+
Original task: \${ultraworkState.original_prompt}
|
|
1216
|
+
|
|
1217
|
+
Continue working in ultrawork mode until all tasks are complete.
|
|
1218
|
+
|
|
1219
|
+
</session-restore>
|
|
1220
|
+
|
|
1221
|
+
---
|
|
1222
|
+
\`);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// Check for incomplete todos
|
|
1226
|
+
const todosDir = join(homedir(), '.claude', 'todos');
|
|
1227
|
+
const incompleteCount = countIncompleteTodos(todosDir);
|
|
1228
|
+
|
|
1229
|
+
if (incompleteCount > 0) {
|
|
1230
|
+
messages.push(\`<session-restore>
|
|
1231
|
+
|
|
1232
|
+
[PENDING TASKS DETECTED]
|
|
1233
|
+
|
|
1234
|
+
You have \${incompleteCount} incomplete tasks from a previous session.
|
|
1235
|
+
Please continue working on these tasks.
|
|
1236
|
+
|
|
1237
|
+
</session-restore>
|
|
1238
|
+
|
|
1239
|
+
---
|
|
1240
|
+
\`);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
if (messages.length > 0) {
|
|
1244
|
+
console.log(JSON.stringify({ continue: true, message: messages.join('\\n') }));
|
|
1245
|
+
} else {
|
|
1246
|
+
console.log(JSON.stringify({ continue: true }));
|
|
1247
|
+
}
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
console.log(JSON.stringify({ continue: true }));
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
605
1253
|
main();
|
|
606
1254
|
`;
|
|
607
1255
|
// =============================================================================
|
|
@@ -623,12 +1271,22 @@ export const HOOKS_SETTINGS_CONFIG_BASH = {
|
|
|
623
1271
|
]
|
|
624
1272
|
}
|
|
625
1273
|
],
|
|
1274
|
+
SessionStart: [
|
|
1275
|
+
{
|
|
1276
|
+
hooks: [
|
|
1277
|
+
{
|
|
1278
|
+
type: "command",
|
|
1279
|
+
command: "bash $HOME/.claude/hooks/session-start.sh"
|
|
1280
|
+
}
|
|
1281
|
+
]
|
|
1282
|
+
}
|
|
1283
|
+
],
|
|
626
1284
|
Stop: [
|
|
627
1285
|
{
|
|
628
1286
|
hooks: [
|
|
629
1287
|
{
|
|
630
1288
|
type: "command",
|
|
631
|
-
command: "bash $HOME/.claude/hooks/
|
|
1289
|
+
command: "bash $HOME/.claude/hooks/persistent-mode.sh"
|
|
632
1290
|
}
|
|
633
1291
|
]
|
|
634
1292
|
}
|
|
@@ -655,14 +1313,26 @@ export const HOOKS_SETTINGS_CONFIG_NODE = {
|
|
|
655
1313
|
]
|
|
656
1314
|
}
|
|
657
1315
|
],
|
|
1316
|
+
SessionStart: [
|
|
1317
|
+
{
|
|
1318
|
+
hooks: [
|
|
1319
|
+
{
|
|
1320
|
+
type: "command",
|
|
1321
|
+
command: isWindows()
|
|
1322
|
+
? 'node "%USERPROFILE%\\.claude\\hooks\\session-start.mjs"'
|
|
1323
|
+
: 'node "$HOME/.claude/hooks/session-start.mjs"'
|
|
1324
|
+
}
|
|
1325
|
+
]
|
|
1326
|
+
}
|
|
1327
|
+
],
|
|
658
1328
|
Stop: [
|
|
659
1329
|
{
|
|
660
1330
|
hooks: [
|
|
661
1331
|
{
|
|
662
1332
|
type: "command",
|
|
663
1333
|
command: isWindows()
|
|
664
|
-
? 'node "%USERPROFILE%\\.claude\\hooks\\
|
|
665
|
-
: 'node "$HOME/.claude/hooks/
|
|
1334
|
+
? 'node "%USERPROFILE%\\.claude\\hooks\\persistent-mode.mjs"'
|
|
1335
|
+
: 'node "$HOME/.claude/hooks/persistent-mode.mjs"'
|
|
666
1336
|
}
|
|
667
1337
|
]
|
|
668
1338
|
}
|
|
@@ -688,14 +1358,18 @@ export const HOOKS_SETTINGS_CONFIG = HOOKS_SETTINGS_CONFIG_BASH;
|
|
|
688
1358
|
*/
|
|
689
1359
|
export const HOOK_SCRIPTS_BASH = {
|
|
690
1360
|
'keyword-detector.sh': KEYWORD_DETECTOR_SCRIPT,
|
|
691
|
-
'stop-continuation.sh': STOP_CONTINUATION_SCRIPT
|
|
1361
|
+
'stop-continuation.sh': STOP_CONTINUATION_SCRIPT,
|
|
1362
|
+
'persistent-mode.sh': PERSISTENT_MODE_SCRIPT,
|
|
1363
|
+
'session-start.sh': SESSION_START_SCRIPT
|
|
692
1364
|
};
|
|
693
1365
|
/**
|
|
694
1366
|
* Node.js hook scripts (Cross-platform)
|
|
695
1367
|
*/
|
|
696
1368
|
export const HOOK_SCRIPTS_NODE = {
|
|
697
1369
|
'keyword-detector.mjs': KEYWORD_DETECTOR_SCRIPT_NODE,
|
|
698
|
-
'stop-continuation.mjs': STOP_CONTINUATION_SCRIPT_NODE
|
|
1370
|
+
'stop-continuation.mjs': STOP_CONTINUATION_SCRIPT_NODE,
|
|
1371
|
+
'persistent-mode.mjs': PERSISTENT_MODE_SCRIPT_NODE,
|
|
1372
|
+
'session-start.mjs': SESSION_START_SCRIPT_NODE
|
|
699
1373
|
};
|
|
700
1374
|
/**
|
|
701
1375
|
* Get the appropriate hook scripts for the current platform
|