
Nav Loop
- 2 installs
- 32 repo stars
- Updated January 23, 2026
- dkyazzentwatwa/supernavigator
Runs a task iteratively until complete with structured completion signals, stagnation detection, and a dual-condition exit gate.
About
Runs tasks iteratively until complete using structured status signals, stagnation detection, and an exit gate. A developer uses it for autonomous run-until-done execution.
- Runs tasks iteratively with structured completion signals
- Includes stagnation detection and dual-condition exit gate
Nav Loop by the numbers
- 2 all-time installs (skills.sh)
- Ranked #1,842 of 2,719 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/dkyazzentwatwa/supernavigator --skill nav-loopAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 32 |
| Last updated | January 23, 2026 |
| Repository | dkyazzentwatwa/supernavigator ↗ |
What it does
Runs a task iteratively until complete with structured completion signals, stagnation detection, and a dual-condition exit gate.
Files
Navigator Loop Skill
Execute tasks iteratively until completion with structured signals, stagnation detection, and dual-condition exit gates.
Why This Exists
Traditional AI coding requires manual "keep going" prompts. Navigator Loop provides:
- Structured completion signals (NAVIGATOR_STATUS block)
- Dual-condition exit gate (heuristics + explicit signal)
- Stagnation detection (circuit breaker for stuck loops)
- Progress visibility (phases: INIT → RESEARCH → IMPL → VERIFY → COMPLETE)
Based on Ralph's autonomous loop innovations, adapted for Navigator's context-efficient architecture.
When to Invoke
Auto-invoke when:
- User says "run until done", "keep going until complete"
- User says "iterate until finished", "autonomous mode"
- User says "loop mode", "don't stop until done"
- Task document has
loop_mode: true
DO NOT invoke if:
- Single-step task (no iteration needed)
- User says "just do this once"
- Already in loop mode (prevent nested loops)
- User explicitly disabled loop mode
Configuration
Loop mode settings in .agent/.nav-config.json:
{
"loop_mode": {
"enabled": false,
"max_iterations": 5,
"stagnation_threshold": 3,
"exit_requires_explicit_signal": true,
"show_status_block": true
}
}Options:
enabled: Default state for new tasksmax_iterations: Hard cap to prevent infinite loops (1-20)stagnation_threshold: Same-state count before pause (2-5)exit_requires_explicit_signal: Require EXIT_SIGNAL alongside heuristics
Execution Steps
Step 1: Initialize Loop State
Load configuration:
python3 functions/phase_detector.py --initInitialize tracking variables:
iteration = 1
max_iterations = config.loop_mode.max_iterations or 5
stagnation_threshold = config.loop_mode.stagnation_threshold or 3
hash_history = []
phase = "INIT"Display loop start:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
LOOP MODE ACTIVATED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Task: {TASK_DESCRIPTION}
Max iterations: {max_iterations}
Stagnation threshold: {stagnation_threshold}
Starting iteration 1...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Step 2: Execute Iteration
Perform task work based on current phase:
| Phase | Actions |
|---|---|
| INIT | Load context, understand requirements |
| RESEARCH | Explore codebase, find patterns |
| IMPL | Write code, make changes |
| VERIFY | Run tests, validate functionality |
| COMPLETE | All indicators met, ready to exit |
Track changes during iteration:
- Files read (for RESEARCH detection)
- Files changed (for IMPL detection)
- Tests run (for VERIFY detection)
- Commits made (for completion indicator)
Step 3: Generate Status Block
After each iteration, generate NAVIGATOR_STATUS:
python3 functions/status_generator.py \
--phase "{phase}" \
--iteration "{iteration}" \
--max-iterations "{max_iterations}" \
--indicators "{indicators_json}" \
--state-hash "{current_hash}" \
--prev-hash "{previous_hash}" \
--stagnation-count "{stagnation_count}"Display status block:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
NAVIGATOR_STATUS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Phase: {PHASE}
Iteration: {N}/{MAX}
Progress: {PERCENT}%
Completion Indicators:
[{x or space}] Code changes committed
[{x or space}] Tests passing
[{x or space}] Documentation updated
[{x or space}] Ticket closed
[{x or space}] Marker created
Exit Conditions:
Heuristics: {MET}/{TOTAL} (need 2+)
EXIT_SIGNAL: {true/false}
State Hash: {HASH}
Previous Hash: {PREV_HASH}
Stagnation: {COUNT}/{THRESHOLD}
Next Action: {NEXT_ACTION}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Step 4: Check Stagnation
Calculate state hash:
python3 functions/stagnation_detector.py \
--phase "{phase}" \
--indicators "{indicators_json}" \
--files-changed "{files_json}" \
--history "{hash_history_json}"If stagnation detected (same hash for N iterations):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
STAGNATION DETECTED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Same state detected for {N} consecutive iterations.
Current State:
Phase: {PHASE}
Indicators: {MET}/{TOTAL}
Last Action: {LAST_ACTION}
Possible causes:
1. Blocked by external dependency
2. Unclear requirements
3. Test failures preventing progress
4. Missing context or permissions
Options:
1. [Continue] - Try one more iteration
2. [Clarify] - Explain what's blocking
3. [Abort] - End loop, manual intervention
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Use AskUserQuestion for choice:
- Continue: Reset stagnation counter, continue loop
- Clarify: User explains blocker, incorporate and continue
- Abort: Exit loop with partial completion marker
Step 5: Check Exit Conditions
Evaluate dual-condition gate:
python3 functions/exit_gate.py \
--indicators "{indicators_json}" \
--exit-signal "{exit_signal}" \
--require-explicit "{config.exit_requires_explicit_signal}"Exit conditions: 1. Heuristics: At least 2 completion indicators met 2. EXIT_SIGNAL: Explicit signal that task is complete
Completion indicators (mapped from autonomous protocol):
code_committed: Changes committed to gittests_passing: Test suite passes (exit code 0)docs_updated: Documentation files changedticket_closed: PM tool ticket marked donemarker_created: Completion marker exists
Exit decision logic:
IF heuristics >= 2 AND exit_signal == true:
→ EXIT: Task complete
ELIF heuristics >= 2 AND exit_signal == false:
→ CONTINUE: Awaiting explicit completion signal
ELIF exit_signal == true AND heuristics < 2:
→ BLOCKED: Cannot exit with insufficient indicators
ELSE:
→ CONTINUE: More work neededStep 6: Handle Max Iterations
If iteration >= max_iterations:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
MAX ITERATIONS REACHED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Completed {MAX} iterations without full completion.
Current State:
Phase: {PHASE}
Indicators: {MET}/{TOTAL}
EXIT_SIGNAL: {true/false}
Progress made:
- {PROGRESS_ITEM_1}
- {PROGRESS_ITEM_2}
Options:
1. [Extend] - Add 3 more iterations
2. [Complete] - Accept current state as done
3. [Abort] - Exit without completion
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Step 7: Complete Loop
When exit conditions met, display completion:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
LOOP COMPLETE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Task: {TASK_DESCRIPTION}
Iterations: {FINAL_COUNT}/{MAX}
Final Phase: COMPLETE
Completion Indicators:
[x] Code changes committed
[x] Tests passing
[x] Documentation updated
[ ] Ticket closed (skipped - no PM tool)
[x] Marker created
Exit Conditions:
Heuristics: 4/5 (passed)
EXIT_SIGNAL: true (passed)
Summary:
- {KEY_CHANGE_1}
- {KEY_CHANGE_2}
- {KEY_CHANGE_3}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Execute autonomous completion protocol: 1. Commit changes (if not already) 2. Archive task documentation 3. Close ticket (if PM configured) 4. Create completion marker (with loop state) 5. Suggest compact
---
Setting EXIT_SIGNAL
The EXIT_SIGNAL is set explicitly by Claude when:
- All primary task requirements are met
- Code is functional and tested
- No obvious remaining work
How to signal completion:
I've completed the implementation. All requirements met.
EXIT_SIGNAL: trueThis explicit declaration prevents premature exits when heuristics are met but work remains.
---
Phase Detection
Phases auto-detected based on context:
def detect_phase(context):
# COMPLETE: Exit conditions met
if indicators_met >= 4 and exit_signal:
return "COMPLETE"
# VERIFY: Tests running or recently run
if context.tests_running or context.test_exit_code is not None:
return "VERIFY"
# IMPL: Files being modified
if context.files_changed:
return "IMPL"
# RESEARCH: Reading files, searching
if context.files_read and not context.files_changed:
return "RESEARCH"
# INIT: Default starting state
return "INIT"---
Integration with Navigator
With Autonomous Completion
Loop mode enhances (not replaces) the autonomous protocol:
- Completion indicators map to autonomous steps
- EXIT_SIGNAL triggers autonomous completion
- Marker includes loop state for restoration
With nav-diagnose
Stagnation triggers nav-diagnose quality check:
- 3 same-state loops = potential quality issue
- nav-diagnose helps identify root cause
- Re-anchoring can resolve stuck loops
With nav-marker
Markers capture loop state:
- Current iteration and max
- Phase at time of marker
- State hash for continuity
- Completion indicators status
With ToM Features
Loop mode respects ToM configuration:
- Verification checkpoints still apply in VERIFY phase
- Profile preferences affect communication style
- Belief anchors can help clarify stuck states
---
Predefined Functions
functions/status_generator.py
Generates formatted NAVIGATOR_STATUS block.
functions/exit_gate.py
Evaluates dual-condition exit (heuristics + explicit signal).
functions/stagnation_detector.py
Calculates state hash and detects consecutive same-states.
functions/phase_detector.py
Auto-detects current task phase from context.
---
Error Handling
Config not found:
Loop mode config not found in .nav-config.json.
Using defaults: max_iterations=5, stagnation_threshold=3Function execution fails:
- Fall back to manual evaluation
- Log error but don't interrupt loop
- Continue with best-effort phase detection
User aborts mid-loop:
- Create partial completion marker
- Document progress made
- List remaining work
---
Success Criteria
Loop mode succeeds when:
- [ ] Task completes within max_iterations
- [ ] No stagnation pauses (or resolved quickly)
- [ ] EXIT_SIGNAL + heuristics both satisfied
- [ ] Completion marker includes loop state
- [ ] User sees clear progress each iteration
---
Examples
Example 1: Simple Feature
User: "Run until done: add isPrime function with tests"
Iteration 1 (INIT → RESEARCH):
- Read existing math utils
- Found test patterns
Iteration 2 (IMPL):
- Created isPrime function
- Created test file
Iteration 3 (VERIFY):
- Ran tests: PASS
- Committed changes
EXIT_SIGNAL: true
→ Loop complete in 3 iterationsExample 2: Stagnation Recovery
User: "Run until done: fix authentication bug"
Iteration 1-3 (IMPL):
- Same changes attempted
- Tests still failing
- State hash unchanged
→ STAGNATION DETECTED
User: "The test needs a mock for the auth service"
Iteration 4 (IMPL):
- Added mock
- Tests pass
EXIT_SIGNAL: true
→ Loop complete in 4 iterations---
Limitations
Cannot handle:
- External blockers (waiting for API, permissions)
- Subjective completion criteria ("make it look nice")
- Tasks requiring human judgment mid-loop
Should not use for:
- Quick fixes (single iteration sufficient)
- Exploratory work (no clear completion state)
- Tasks with security implications (need human review)
---
This skill provides Ralph-style "run until done" capability while maintaining Navigator's context efficiency and ToM integration.
#!/usr/bin/env python3
"""
Dual-condition exit gate for Navigator loop mode.
Evaluates whether loop should exit based on:
1. Heuristics: At least N completion indicators met
2. Explicit signal: EXIT_SIGNAL must be true
Usage:
python3 exit_gate.py \
--indicators '{"code_committed": true, "tests_passing": true}' \
--exit-signal \
--min-heuristics 2
Output:
JSON with exit decision and reasoning
"""
import argparse
import json
import sys
from typing import Tuple
def count_indicators(indicators: dict) -> Tuple[int, int]:
"""Count met indicators vs total."""
if not indicators:
return 0, 5
met = sum(1 for v in indicators.values() if v)
total = len(indicators)
return met, total
def evaluate_exit(
indicators: dict,
exit_signal: bool,
min_heuristics: int = 2,
require_explicit: bool = True
) -> dict:
"""
Evaluate dual-condition exit gate.
Returns dict with:
- should_exit: bool
- reason: str
- heuristics_met: int
- heuristics_total: int
- exit_signal: bool
- blocked_reason: str or None
"""
met, total = count_indicators(indicators)
heuristics_satisfied = met >= min_heuristics
result = {
"heuristics_met": met,
"heuristics_total": total,
"heuristics_satisfied": heuristics_satisfied,
"exit_signal": exit_signal,
"min_required": min_heuristics,
"require_explicit": require_explicit
}
# Dual-condition evaluation
if heuristics_satisfied and exit_signal:
result["should_exit"] = True
result["reason"] = f"EXIT: {met}/{total} heuristics + explicit signal"
result["blocked_reason"] = None
elif exit_signal and not heuristics_satisfied:
result["should_exit"] = False
result["reason"] = f"BLOCKED: Exit signal but only {met}/{total} heuristics"
result["blocked_reason"] = "Insufficient completion indicators"
elif heuristics_satisfied and not exit_signal:
if require_explicit:
result["should_exit"] = False
result["reason"] = f"CONTINUE: {met}/{total} heuristics, awaiting EXIT_SIGNAL"
result["blocked_reason"] = "Awaiting explicit completion signal"
else:
# Legacy mode: exit on heuristics alone
result["should_exit"] = True
result["reason"] = f"EXIT: {met}/{total} heuristics (explicit signal not required)"
result["blocked_reason"] = None
else:
result["should_exit"] = False
result["reason"] = f"CONTINUE: {met}/{total} heuristics, no exit signal"
result["blocked_reason"] = "More work needed"
return result
def main():
parser = argparse.ArgumentParser(
description="Evaluate dual-condition exit gate"
)
parser.add_argument("--indicators", default="{}",
help="JSON object of indicator states")
parser.add_argument("--exit-signal", action="store_true",
help="Whether EXIT_SIGNAL was explicitly set")
parser.add_argument("--min-heuristics", type=int, default=2,
help="Minimum indicators required (default: 2)")
parser.add_argument("--no-require-explicit", action="store_true",
help="Don't require explicit EXIT_SIGNAL")
parser.add_argument("--output", choices=["json", "text"], default="json",
help="Output format")
args = parser.parse_args()
try:
indicators = json.loads(args.indicators)
except json.JSONDecodeError:
print("Error: Invalid JSON for indicators", file=sys.stderr)
return 1
result = evaluate_exit(
indicators=indicators,
exit_signal=args.exit_signal,
min_heuristics=args.min_heuristics,
require_explicit=not args.no_require_explicit
)
if args.output == "json":
print(json.dumps(result, indent=2))
else:
print(f"Decision: {'EXIT' if result['should_exit'] else 'CONTINUE'}")
print(f"Reason: {result['reason']}")
if result['blocked_reason']:
print(f"Blocked: {result['blocked_reason']}")
# Exit code: 0 = should exit, 1 = should continue
return 0 if result["should_exit"] else 1
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python3
"""
Auto-detect current task phase for Navigator loop mode.
Phases:
- INIT: Loading context, understanding requirements
- RESEARCH: Exploring codebase, finding patterns
- IMPL: Writing code, making changes
- VERIFY: Running tests, validating functionality
- COMPLETE: All indicators met, ready to exit
Usage:
python3 phase_detector.py \
--files-read '["src/auth.ts", "README.md"]' \
--files-changed '["src/login.ts"]' \
--tests-running \
--indicators '{"code_committed": true, "tests_passing": true}'
Output:
JSON with detected phase and confidence
"""
import argparse
import json
import sys
from typing import List, Optional
def detect_phase(
files_read: List[str],
files_changed: List[str],
tests_running: bool = False,
test_exit_code: Optional[int] = None,
indicators: dict = None,
exit_signal: bool = False
) -> dict:
"""
Auto-detect current task phase from context.
Returns dict with:
- phase: str
- confidence: float (0-1)
- reason: str
- next_expected: str
"""
indicators = indicators or {}
met_count = sum(1 for v in indicators.values() if v)
# COMPLETE: Exit conditions met
if met_count >= 4 and exit_signal:
return {
"phase": "COMPLETE",
"confidence": 1.0,
"reason": f"Exit conditions met ({met_count}/5 indicators + EXIT_SIGNAL)",
"next_expected": "Execute autonomous completion protocol"
}
# VERIFY: Tests running or recently run
if tests_running:
return {
"phase": "VERIFY",
"confidence": 0.95,
"reason": "Tests currently running",
"next_expected": "Wait for test results, then evaluate"
}
if test_exit_code is not None:
if test_exit_code == 0:
return {
"phase": "VERIFY",
"confidence": 0.9,
"reason": f"Tests completed (exit code: {test_exit_code})",
"next_expected": "Commit changes if tests pass"
}
else:
return {
"phase": "IMPL",
"confidence": 0.85,
"reason": f"Tests failed (exit code: {test_exit_code})",
"next_expected": "Fix failing tests"
}
# IMPL: Files being modified
if files_changed:
return {
"phase": "IMPL",
"confidence": 0.9,
"reason": f"Files modified: {len(files_changed)} file(s)",
"next_expected": "Continue implementation or run tests"
}
# RESEARCH: Reading files, no changes yet
if files_read and not files_changed:
return {
"phase": "RESEARCH",
"confidence": 0.85,
"reason": f"Files read: {len(files_read)} file(s), no changes yet",
"next_expected": "Start implementation based on research"
}
# INIT: Default starting state
return {
"phase": "INIT",
"confidence": 0.7,
"reason": "No significant activity detected",
"next_expected": "Load context and understand requirements"
}
def main():
parser = argparse.ArgumentParser(
description="Detect current task phase"
)
parser.add_argument("--files-read", default="[]",
help="JSON array of files read this iteration")
parser.add_argument("--files-changed", default="[]",
help="JSON array of files changed this iteration")
parser.add_argument("--tests-running", action="store_true",
help="Whether tests are currently running")
parser.add_argument("--test-exit-code", type=int, default=None,
help="Exit code from last test run")
parser.add_argument("--indicators", default="{}",
help="JSON object of completion indicator states")
parser.add_argument("--exit-signal", action="store_true",
help="Whether EXIT_SIGNAL was set")
parser.add_argument("--init", action="store_true",
help="Initialize fresh phase detection")
parser.add_argument("--output", choices=["json", "text"], default="json",
help="Output format")
args = parser.parse_args()
# Handle --init flag
if args.init:
result = {
"phase": "INIT",
"confidence": 1.0,
"reason": "Fresh initialization",
"next_expected": "Load context and understand task"
}
else:
try:
files_read = json.loads(args.files_read)
files_changed = json.loads(args.files_changed)
indicators = json.loads(args.indicators)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON input - {e}", file=sys.stderr)
return 1
result = detect_phase(
files_read=files_read,
files_changed=files_changed,
tests_running=args.tests_running,
test_exit_code=args.test_exit_code,
indicators=indicators,
exit_signal=args.exit_signal
)
if args.output == "json":
print(json.dumps(result, indent=2))
else:
print(f"Phase: {result['phase']}")
print(f"Confidence: {result['confidence']:.0%}")
print(f"Reason: {result['reason']}")
print(f"Next: {result['next_expected']}")
return 0
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python3
"""
Stagnation detection for Navigator loop mode.
Detects when the loop is stuck in the same state by comparing
state hashes across iterations.
Usage:
python3 stagnation_detector.py \
--phase "IMPL" \
--indicators '{"code_committed": false}' \
--files-changed '["src/auth.ts"]' \
--history '["abc123", "abc123"]' \
--threshold 3
Output:
JSON with stagnation status and hash
"""
import argparse
import hashlib
import json
import sys
from typing import List, Tuple
def calculate_state_hash(
phase: str,
indicators: dict,
files_changed: List[str],
error_state: str = None
) -> str:
"""
Generate hash representing current state.
Same hash = same state = potential stagnation.
"""
state_components = {
"phase": phase,
"indicators": sorted([
k for k, v in indicators.items() if v
]),
"files_changed": sorted(files_changed) if files_changed else [],
"error_state": error_state
}
state_json = json.dumps(state_components, sort_keys=True)
return hashlib.md5(state_json.encode()).hexdigest()[:6]
def count_consecutive_same(history: List[str], current_hash: str) -> int:
"""Count consecutive occurrences of current hash in history."""
consecutive = 1 # Current counts as 1
for prev_hash in reversed(history):
if prev_hash == current_hash:
consecutive += 1
else:
break
return consecutive
def check_stagnation(
current_hash: str,
history: List[str],
threshold: int = 3
) -> Tuple[bool, int]:
"""
Check if loop is stagnating.
Returns:
(is_stagnant, consecutive_count)
"""
consecutive = count_consecutive_same(history, current_hash)
is_stagnant = consecutive >= threshold
return is_stagnant, consecutive
def detect_stagnation(
phase: str,
indicators: dict,
files_changed: List[str],
history: List[str],
threshold: int = 3,
error_state: str = None
) -> dict:
"""
Full stagnation detection.
Returns dict with:
- current_hash: str
- is_stagnant: bool
- consecutive_count: int
- threshold: int
- recommendation: str
"""
current_hash = calculate_state_hash(
phase=phase,
indicators=indicators,
files_changed=files_changed,
error_state=error_state
)
is_stagnant, consecutive = check_stagnation(
current_hash=current_hash,
history=history,
threshold=threshold
)
# Generate recommendation
if is_stagnant:
recommendation = "PAUSE: Same state detected. User intervention needed."
elif consecutive >= threshold - 1:
recommendation = "WARNING: Approaching stagnation threshold."
else:
recommendation = "OK: State is changing normally."
return {
"current_hash": current_hash,
"previous_hash": history[-1] if history else None,
"is_stagnant": is_stagnant,
"consecutive_count": consecutive,
"threshold": threshold,
"recommendation": recommendation,
"state_components": {
"phase": phase,
"met_indicators": [k for k, v in indicators.items() if v],
"files_changed_count": len(files_changed) if files_changed else 0
}
}
def main():
parser = argparse.ArgumentParser(
description="Detect loop stagnation"
)
parser.add_argument("--phase", default="INIT",
help="Current phase")
parser.add_argument("--indicators", default="{}",
help="JSON object of indicator states")
parser.add_argument("--files-changed", default="[]",
help="JSON array of changed files")
parser.add_argument("--history", default="[]",
help="JSON array of previous state hashes")
parser.add_argument("--threshold", type=int, default=3,
help="Stagnation threshold (default: 3)")
parser.add_argument("--error-state", default=None,
help="Current error state if any")
parser.add_argument("--output", choices=["json", "text"], default="json",
help="Output format")
args = parser.parse_args()
try:
indicators = json.loads(args.indicators)
files_changed = json.loads(args.files_changed)
history = json.loads(args.history)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON input - {e}", file=sys.stderr)
return 1
result = detect_stagnation(
phase=args.phase,
indicators=indicators,
files_changed=files_changed,
history=history,
threshold=args.threshold,
error_state=args.error_state
)
if args.output == "json":
print(json.dumps(result, indent=2))
else:
print(f"Hash: {result['current_hash']}")
print(f"Stagnant: {result['is_stagnant']}")
print(f"Consecutive: {result['consecutive_count']}/{result['threshold']}")
print(f"Status: {result['recommendation']}")
# Exit code: 1 = stagnant, 0 = OK
return 1 if result["is_stagnant"] else 0
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python3
"""
Generate NAVIGATOR_STATUS block for loop mode.
Usage:
python3 status_generator.py \
--phase "IMPL" \
--iteration 2 \
--max-iterations 5 \
--indicators '{"code_committed": true, "tests_passing": true}' \
--state-hash "a7b3c9" \
--prev-hash "a7b3c9" \
--stagnation-count 1 \
--next-action "Run tests"
"""
import argparse
import json
import sys
def calculate_progress(phase: str, indicators: dict) -> int:
"""Calculate progress percentage based on phase and indicators."""
phase_weights = {
"INIT": 10,
"RESEARCH": 25,
"IMPL": 50,
"VERIFY": 75,
"COMPLETE": 100
}
base = phase_weights.get(phase, 0)
indicator_count = sum(1 for v in indicators.values() if v)
indicator_bonus = (indicator_count / max(len(indicators), 1)) * 25
return min(100, int(base + indicator_bonus))
def format_indicators(indicators: dict) -> str:
"""Format completion indicators with checkboxes."""
indicator_labels = {
"code_committed": "Code changes committed",
"tests_passing": "Tests passing",
"docs_updated": "Documentation updated",
"ticket_closed": "Ticket closed",
"marker_created": "Marker created"
}
lines = []
for key, label in indicator_labels.items():
checked = indicators.get(key, False)
mark = "x" if checked else " "
lines.append(f" [{mark}] {label}")
return "\n".join(lines)
def count_met_indicators(indicators: dict) -> tuple:
"""Count met vs total indicators."""
met = sum(1 for v in indicators.values() if v)
total = len(indicators) if indicators else 5
return met, total
def generate_status_block(
phase: str,
iteration: int,
max_iterations: int,
indicators: dict,
state_hash: str,
prev_hash: str,
stagnation_count: int,
stagnation_threshold: int = 3,
exit_signal: bool = False,
next_action: str = "Continue working"
) -> str:
"""Generate formatted NAVIGATOR_STATUS block."""
progress = calculate_progress(phase, indicators)
indicator_display = format_indicators(indicators)
met, total = count_met_indicators(indicators)
status = f"""
NAVIGATOR_STATUS
{'=' * 50}
Phase: {phase}
Iteration: {iteration}/{max_iterations}
Progress: {progress}%
Completion Indicators:
{indicator_display}
Exit Conditions:
Heuristics: {met}/{total} (need 2+)
EXIT_SIGNAL: {str(exit_signal).lower()}
State Hash: {state_hash}
Previous Hash: {prev_hash}
Stagnation: {stagnation_count}/{stagnation_threshold}
Next Action: {next_action}
{'=' * 50}
"""
return status.strip()
def main():
parser = argparse.ArgumentParser(
description="Generate NAVIGATOR_STATUS block"
)
parser.add_argument("--phase", default="INIT",
choices=["INIT", "RESEARCH", "IMPL", "VERIFY", "COMPLETE"])
parser.add_argument("--iteration", type=int, default=1)
parser.add_argument("--max-iterations", type=int, default=5)
parser.add_argument("--indicators", default="{}")
parser.add_argument("--state-hash", default="000000")
parser.add_argument("--prev-hash", default="000000")
parser.add_argument("--stagnation-count", type=int, default=0)
parser.add_argument("--stagnation-threshold", type=int, default=3)
parser.add_argument("--exit-signal", action="store_true")
parser.add_argument("--next-action", default="Continue working")
args = parser.parse_args()
try:
indicators = json.loads(args.indicators)
except json.JSONDecodeError:
indicators = {}
status = generate_status_block(
phase=args.phase,
iteration=args.iteration,
max_iterations=args.max_iterations,
indicators=indicators,
state_hash=args.state_hash,
prev_hash=args.prev_hash,
stagnation_count=args.stagnation_count,
stagnation_threshold=args.stagnation_threshold,
exit_signal=args.exit_signal,
next_action=args.next_action
)
print(status)
return 0
if __name__ == "__main__":
sys.exit(main())