#Copyright 2026 GoalSprint
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
import json
import datetime
import os
import sys
import time
# ================= CONFIGURATION =================
GLOBAL_LAUNCH_DATE = datetime.date(2026, 3, 1)
GLOBAL_END_DATE = datetime.date(2027, 1, 31)
TOTAL_POSSIBLE_REST_DAYS = 22
DATA_FILE = "goalsprint_data.json"
DEMO_OVERRIDE = False
try:
from colorama import init, Fore, Style, Back
init(autoreset=True)
except ImportError:
class Fore: RED=GREEN=YELLOW=CYAN=WHITE=MAGENTA=RESET="";
class Style: BRIGHT=DIM=RESET_ALL="";
class Back: RED="";
def clear_screen():
os.system('cls' if os.name == 'nt' else 'clear')
def get_today():
return datetime.date.today()
def calculate_initial_balance():
start = GLOBAL_LAUNCH_DATE
today = get_today()
if today < start: return TOTAL_POSSIBLE_REST_DAYS
checkpoints_missed = 0
curr = start
while curr < today:
if curr.day == 1 or curr.day == 15:
checkpoints_missed += 1
curr += datetime.timedelta(days=1)
return max(0, TOTAL_POSSIBLE_REST_DAYS - checkpoints_missed)
def load_data():
if not os.path.exists(DATA_FILE):
starting_rest = calculate_initial_balance()
return {
"rest_days": starting_rest,
"last_log": None,
"streak": 0,
"status": "ACTIVE",
"joined_date": str(get_today())
}
with open(DATA_FILE, 'r') as f:
data = json.load(f)
if "joined_date" not in data:
data["joined_date"] = str(GLOBAL_LAUNCH_DATE)
return data
def save_data(data):
with open(DATA_FILE, 'w') as f:
json.dump(data, f, indent=4)
def wipe_data():
clear_screen()
print(Back.RED + Fore.WHITE + "\n" + "#" * 50)
print(Back.RED + Fore.WHITE + " CRITICAL FAILURE ".center(50))
print(Back.RED + Fore.WHITE + "#" * 50 + Style.RESET_ALL)
print(Fore.RED + "\nYou have exhausted your rest days.")
print("Protocol Failed. All data has been wiped.")
time.sleep(2)
if os.path.exists(DATA_FILE):
os.remove(DATA_FILE)
print(Fore.RED + "\n[DATA WIPED]")
sys.exit()
def show_rules():
clear_screen()
print(Style.BRIGHT + Fore.YELLOW + "📜 THE MANIFESTO & LAWS\n")
print(Fore.WHITE + "1. TIME WINDOW: 00:00 - 23:59 Local Time.")
print(Fore.WHITE + "2. GHOST PROTOCOL: Miss a day? Auto-logged as REST.")
print(Fore.WHITE + "3. REST POOL: 22 Days. Hit 0? Data Wiped.")
print(Fore.WHITE + "4. LATE JOIN: You lose rest days for missed checkpoints.")
print(Fore.WHITE + "5. SILENCE: Validation is cheap. Discipline is silent.")
print("\n")
input(Fore.CYAN + "Press Enter to return...")
def check_ghost_protocol(data):
if not data["last_log"]: return data
last_date = datetime.date.fromisoformat(data["last_log"])
today = get_today()
delta = (today - last_date).days
if delta > 1:
missed = delta - 1
data['rest_days'] -= missed
yesterday = today - datetime.timedelta(days=1)
data['last_log'] = str(yesterday)
if data['rest_days'] < 0: wipe_data()
save_data(data)
return data
def main():
while True:
clear_screen()
today = get_today()
if not DEMO_OVERRIDE and today < GLOBAL_LAUNCH_DATE:
print(Fore.RED + "🔒 SYSTEM LOCKED")
print(f"GoalSprint begins on {GLOBAL_LAUNCH_DATE}.")
input()
return
data = load_data()
data = check_ghost_protocol(data)
# --- UI UPDATE TO MATCH ANDROID ---
print("\n" + Style.BRIGHT + Fore.WHITE + " Goal Sprint")
print(Style.DIM + f" {today.strftime('%B %d, %Y')}\n")
join_date = datetime.date.fromisoformat(data['joined_date'])
days_active_count = (today - join_date).days + 1
days_left_count = (GLOBAL_END_DATE - today).days
if days_active_count < 0: days_active_count = 0
if days_left_count < 0: days_left_count = 0
streak_display = f"{data['streak']} 🔥" if data['streak'] > 0 else f"{data['streak']}"
txt_rest = f" REST DAYS: {data['rest_days']}".ljust(17)
txt_streak = f" STREAK: {streak_display}".ljust(14)
txt_active = f" ACTIVE DAYS: {days_active_count}".ljust(19)
txt_left = f" LEFT DAYS: {days_left_count} ".ljust(18)
box_width = len(txt_rest) + len(txt_streak) + len(txt_active) + len(txt_left) + 5
print(Fore.WHITE + "+" + "-" * (box_width - 2) + "+")
print(f"|{Fore.YELLOW}{txt_rest}{Fore.WHITE}|{Fore.GREEN}{txt_streak}{Fore.WHITE}|{Fore.CYAN}{txt_active}{Fore.WHITE}|{Fore.MAGENTA}{txt_left}{Fore.WHITE}|")
print(Fore.WHITE + "+" + "-" * (box_width - 2) + "+")
print("")
mission_done = data["last_log"] == str(today)
if mission_done:
print(Back.GREEN + Fore.BLACK + " ✅ MISSION ACCOMPLISHED FOR TODAY " + Style.RESET_ALL)
print(Style.DIM + "\n System assumes 00:00 - 23:59 log window.")
else:
print(Fore.CYAN + " AWAITING INPUT (Deadline: 23:59 Local Time)")
print(Fore.WHITE + "\n[1] DONE")
print(Fore.WHITE + "[2] REST")
print(Fore.WHITE + "[3] EXIT")
print(Fore.WHITE + "[4] CAMPAIGN RULES")
choice = input(Fore.CYAN + "\n> ")
if choice == "1":
if mission_done:
print(Fore.RED + "Already logged today.")
time.sleep(1)
else:
confirm = input("Confirm DONE? (y/n): ")
if confirm.lower() == 'y':
data["last_log"] = str(today)
data["streak"] += 1
save_data(data)
print(Fore.GREEN + "Confirmed.")
time.sleep(0.5)
elif choice == "2":
if mission_done:
print(Fore.RED + "Already logged today.")
time.sleep(1)
else:
print(Fore.RED + f"⚠️ This consumes 1 Rest Day. ({data['rest_days']} left)")
confirm = input("Confirm REST? (y/n): ")
if confirm.lower() == 'y':
data["rest_days"] -= 1
data["last_log"] = str(today)
if data["rest_days"] < 0: wipe_data()
save_data(data)
print(Fore.YELLOW + "Rest day taken.")
time.sleep(1)
elif choice == "3": sys.exit()
elif choice == "4": show_rules()
else:
print("Invalid command.")
time.sleep(0.5)
if __name__ == "__main__":
main()