Executor-Tyrant-Framework commited on
Commit
68bf032
Β·
unverified Β·
1 Parent(s): acf2df3

Update recursive_context.py

Browse files
Files changed (1) hide show
  1. recursive_context.py +233 -16
recursive_context.py CHANGED
@@ -398,19 +398,33 @@ class RecursiveContextManager:
398
  return f"STDOUT: {result.stdout}\nSTDERR: {result.stderr}"
399
  except Exception as e:
400
  return f"Execution Error: {e}"
401
- def push_to_github(self, commit_message="Auto-backup from Clawdbot"):
402
- """Pushes the current workspace to the configured GitHub repository."""
 
 
 
403
  token = os.getenv("GITHUB_TOKEN")
404
  repo = os.getenv("GITHUB_REPO")
405
 
406
  if not token or not repo:
407
- return "❌ Error: GITHUB_TOKEN or GITHUB_REPO secret is missing."
 
 
 
 
 
 
 
 
408
 
409
- # authenticated URL
410
- remote_url = f"https://{token}@github.com/{repo}.git"
 
 
 
411
 
412
  try:
413
- # 1. Initialize if needed (Docker containers often lack .git)
414
  if not (self.repo_path / ".git").exists():
415
  subprocess.run(["git", "init"], cwd=self.repo_path, check=True)
416
  subprocess.run(["git", "config", "user.email", "clawdbot@e-t-systems.ai"], cwd=self.repo_path)
@@ -418,7 +432,6 @@ class RecursiveContextManager:
418
  subprocess.run(["git", "branch", "-M", "main"], cwd=self.repo_path)
419
 
420
  # 2. Configure Remote (Idempotent)
421
- # Remove existing remote to ensure token is fresh/correct
422
  subprocess.run(["git", "remote", "remove", "origin"], cwd=self.repo_path, stderr=subprocess.DEVNULL)
423
  subprocess.run(["git", "remote", "add", "origin", remote_url], cwd=self.repo_path, check=True)
424
 
@@ -431,14 +444,16 @@ class RecursiveContextManager:
431
  cwd=self.repo_path, capture_output=True, text=True
432
  )
433
 
434
- # Push (force is safer for a backup mirror to overwrite conflicts)
435
  push_res = subprocess.run(
436
  ["git", "push", "-u", "origin", "main", "--force"],
437
  cwd=self.repo_path, capture_output=True, text=True
438
  )
439
 
440
  if push_res.returncode == 0:
441
- return f"βœ… Successfully pushed to GitHub: https://github.com/{repo}"
 
 
442
  else:
443
  return f"⚠️ Git Push Failed: {push_res.stderr}"
444
 
@@ -447,13 +462,9 @@ class RecursiveContextManager:
447
 
448
  def pull_from_github(self, branch="main"):
449
  """Hard reset: Destroys local changes and pulls clean code from GitHub."""
450
- token = os.getenv("GITHUB_TOKEN")
451
- repo = os.getenv("GITHUB_REPO")
452
-
453
- if not token or not repo:
454
  return "❌ Error: GITHUB_TOKEN or GITHUB_REPO secret is missing."
455
-
456
- remote_url = f"https://{token}@github.com/{repo}.git"
457
 
458
  try:
459
  # 1. Init if missing
@@ -461,7 +472,7 @@ class RecursiveContextManager:
461
  subprocess.run(["git", "init"], cwd=self.repo_path, check=True)
462
  subprocess.run(["git", "remote", "add", "origin", remote_url], cwd=self.repo_path)
463
 
464
- # 2. Fetch and Reset (Destructive but safe for recovery)
465
  subprocess.run(["git", "fetch", "origin"], cwd=self.repo_path, check=True)
466
  res = subprocess.run(
467
  ["git", "reset", "--hard", f"origin/{branch}"],
@@ -475,6 +486,72 @@ class RecursiveContextManager:
475
 
476
  except Exception as e:
477
  return f"❌ Critical Git Error: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
478
  # =====================================================================
479
  # RECURSIVE SEARCH TOOLS
480
  # =====================================================================
@@ -585,3 +662,143 @@ class RecursiveContextManager:
585
 
586
  # 3. Push the entire manifest back to your PRO storage dataset
587
  self.persistence.save_conversations(full_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
  return f"STDOUT: {result.stdout}\nSTDERR: {result.stderr}"
399
  except Exception as e:
400
  return f"Execution Error: {e}"
401
+ # =====================================================================
402
+ # GIT TOOLS (Fixed for Double-URL Issue)
403
+ # =====================================================================
404
+ def _get_authenticated_remote_url(self):
405
+ """Helper to construct the correct authenticated URL."""
406
  token = os.getenv("GITHUB_TOKEN")
407
  repo = os.getenv("GITHUB_REPO")
408
 
409
  if not token or not repo:
410
+ return None
411
+
412
+ # Clean the repo string if it's already a full URL
413
+ if repo.startswith("https://github.com/"):
414
+ repo = repo.replace("https://github.com/", "")
415
+ if repo.endswith(".git"):
416
+ repo = repo[:-4]
417
+
418
+ return f"https://{token}@github.com/{repo}.git"
419
 
420
+ def push_to_github(self, commit_message="Auto-backup from Clawdbot"):
421
+ """Pushes the current workspace to the configured GitHub repository."""
422
+ remote_url = self._get_authenticated_remote_url()
423
+ if not remote_url:
424
+ return "❌ Error: GITHUB_TOKEN or GITHUB_REPO secret is missing."
425
 
426
  try:
427
+ # 1. Initialize if needed
428
  if not (self.repo_path / ".git").exists():
429
  subprocess.run(["git", "init"], cwd=self.repo_path, check=True)
430
  subprocess.run(["git", "config", "user.email", "clawdbot@e-t-systems.ai"], cwd=self.repo_path)
 
432
  subprocess.run(["git", "branch", "-M", "main"], cwd=self.repo_path)
433
 
434
  # 2. Configure Remote (Idempotent)
 
435
  subprocess.run(["git", "remote", "remove", "origin"], cwd=self.repo_path, stderr=subprocess.DEVNULL)
436
  subprocess.run(["git", "remote", "add", "origin", remote_url], cwd=self.repo_path, check=True)
437
 
 
444
  cwd=self.repo_path, capture_output=True, text=True
445
  )
446
 
447
+ # Push (force is safer for a backup mirror)
448
  push_res = subprocess.run(
449
  ["git", "push", "-u", "origin", "main", "--force"],
450
  cwd=self.repo_path, capture_output=True, text=True
451
  )
452
 
453
  if push_res.returncode == 0:
454
+ # Clean URL for display security
455
+ display_url = remote_url.replace(os.getenv("GITHUB_TOKEN"), "TOKEN_HIDDEN")
456
+ return f"βœ… Successfully pushed to GitHub"
457
  else:
458
  return f"⚠️ Git Push Failed: {push_res.stderr}"
459
 
 
462
 
463
  def pull_from_github(self, branch="main"):
464
  """Hard reset: Destroys local changes and pulls clean code from GitHub."""
465
+ remote_url = self._get_authenticated_remote_url()
466
+ if not remote_url:
 
 
467
  return "❌ Error: GITHUB_TOKEN or GITHUB_REPO secret is missing."
 
 
468
 
469
  try:
470
  # 1. Init if missing
 
472
  subprocess.run(["git", "init"], cwd=self.repo_path, check=True)
473
  subprocess.run(["git", "remote", "add", "origin", remote_url], cwd=self.repo_path)
474
 
475
+ # 2. Fetch and Reset
476
  subprocess.run(["git", "fetch", "origin"], cwd=self.repo_path, check=True)
477
  res = subprocess.run(
478
  ["git", "reset", "--hard", f"origin/{branch}"],
 
486
 
487
  except Exception as e:
488
  return f"❌ Critical Git Error: {e}"
489
+
490
+ # =====================================================================
491
+ # WORKING MEMORY NOTEBOOK (Structured List)
492
+ # =====================================================================
493
+ def _load_notebook(self) -> List[Dict]:
494
+ """Internal helper to load notebook JSON."""
495
+ notebook_path = self.repo_path / "memory" / "notebook.json"
496
+ if not notebook_path.exists():
497
+ return []
498
+ try:
499
+ return json.loads(notebook_path.read_text(encoding='utf-8'))
500
+ except:
501
+ return []
502
+
503
+ def _save_notebook(self, notes: List[Dict]):
504
+ """Internal helper to save notebook JSON with audit logging."""
505
+ notebook_path = self.repo_path / "memory" / "notebook.json"
506
+ log_path = self.repo_path / "memory" / "history.log"
507
+
508
+ notebook_path.parent.mkdir(parents=True, exist_ok=True)
509
+ notebook_path.write_text(json.dumps(notes, indent=2), encoding='utf-8')
510
+
511
+ # Audit Log (Metadata only)
512
+ try:
513
+ timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
514
+ with open(log_path, "a") as f:
515
+ f.write(f"[{timestamp}] NOTEBOOK UPDATED | Count: {len(notes)}\n")
516
+ except: pass
517
+
518
+ def notebook_read(self) -> str:
519
+ """Reads the Working Memory notebook."""
520
+ notes = self._load_notebook()
521
+ if not notes:
522
+ return ""
523
+
524
+ display = []
525
+ for i, note in enumerate(notes):
526
+ display.append(f"{i+1}. [{note['timestamp']}] {note['content']}")
527
+ return "\n".join(display)
528
+
529
+ def notebook_add(self, content: str) -> str:
530
+ """Adds a new note to the notebook."""
531
+ notes = self._load_notebook()
532
+
533
+ if len(notes) >= 25:
534
+ return "⚠️ Notebook full (25/25 slots). Please delete obsolete notes first."
535
+
536
+ timestamp = time.strftime("%Y-%m-%d %H:%M")
537
+ notes.append({"timestamp": timestamp, "content": content})
538
+ self._save_notebook(notes)
539
+ return f"βœ… Note added. ({len(notes)}/25 slots used)"
540
+
541
+ def notebook_delete(self, index: int) -> str:
542
+ """Deletes a note by its number (1-based index)."""
543
+ notes = self._load_notebook()
544
+ try:
545
+ idx = int(index) - 1
546
+ if 0 <= idx < len(notes):
547
+ removed = notes.pop(idx)
548
+ self._save_notebook(notes)
549
+ return f"βœ… Deleted note #{index}: '{removed['content'][:30]}...'"
550
+ else:
551
+ return f"❌ Invalid index: {index}. Valid range: 1-{len(notes)}"
552
+ except ValueError:
553
+ return "❌ Index must be a number."
554
+
555
  # =====================================================================
556
  # RECURSIVE SEARCH TOOLS
557
  # =====================================================================
 
662
 
663
  # 3. Push the entire manifest back to your PRO storage dataset
664
  self.persistence.save_conversations(full_data)
665
+
666
+ # =====================================================================
667
+ # GIT TOOLS
668
+ # =====================================================================
669
+ def _get_authenticated_remote_url(self):
670
+ """Helper to construct the correct authenticated URL."""
671
+ token = os.getenv("GITHUB_TOKEN")
672
+ repo = os.getenv("GITHUB_REPO")
673
+
674
+ if not token or not repo:
675
+ return None
676
+
677
+ # Clean the repo string if it's already a full URL
678
+ if repo.startswith("https://github.com/"):
679
+ repo = repo.replace("https://github.com/", "")
680
+ if repo.endswith(".git"):
681
+ repo = repo[:-4]
682
+
683
+ return f"https://{token}@github.com/{repo}.git"
684
+
685
+ def push_to_github(self, commit_message="Auto-backup from Clawdbot"):
686
+ """Pushes the current workspace to the configured GitHub repository."""
687
+ remote_url = self._get_authenticated_remote_url()
688
+ if not remote_url:
689
+ return "❌ Error: GITHUB_TOKEN or GITHUB_REPO secret is missing."
690
+
691
+ try:
692
+ # 1. Initialize if needed
693
+ if not (self.repo_path / ".git").exists():
694
+ subprocess.run(["git", "init"], cwd=self.repo_path, check=True)
695
+ subprocess.run(["git", "config", "user.email", "clawdbot@e-t-systems.ai"], cwd=self.repo_path)
696
+ subprocess.run(["git", "config", "user.name", "Clawdbot"], cwd=self.repo_path)
697
+ subprocess.run(["git", "branch", "-M", "main"], cwd=self.repo_path)
698
+
699
+ # 2. Configure Remote (Idempotent)
700
+ subprocess.run(["git", "remote", "remove", "origin"], cwd=self.repo_path, stderr=subprocess.DEVNULL)
701
+ subprocess.run(["git", "remote", "add", "origin", remote_url], cwd=self.repo_path, check=True)
702
+
703
+ # 3. Add, Commit, Push
704
+ subprocess.run(["git", "add", "."], cwd=self.repo_path, check=True)
705
+
706
+ # Commit (allow empty if nothing changed)
707
+ commit_res = subprocess.run(
708
+ ["git", "commit", "-m", commit_message],
709
+ cwd=self.repo_path, capture_output=True, text=True
710
+ )
711
+
712
+ # Push (force is safer for a backup mirror)
713
+ push_res = subprocess.run(
714
+ ["git", "push", "-u", "origin", "main", "--force"],
715
+ cwd=self.repo_path, capture_output=True, text=True
716
+ )
717
+
718
+ if push_res.returncode == 0:
719
+ repo_name = os.getenv("GITHUB_REPO").replace("https://github.com/", "").replace(".git", "")
720
+ return f"βœ… Successfully pushed to GitHub: https://github.com/{repo_name}"
721
+ else:
722
+ return f"⚠️ Git Push Failed: {push_res.stderr}"
723
+
724
+ except Exception as e:
725
+ return f"❌ Critical Git Error: {e}"
726
+
727
+ def pull_from_github(self, branch="main"):
728
+ """Hard reset: Destroys local changes and pulls clean code from GitHub."""
729
+ remote_url = self._get_authenticated_remote_url()
730
+ if not remote_url:
731
+ return "❌ Error: GITHUB_TOKEN or GITHUB_REPO secret is missing."
732
+
733
+ try:
734
+ # 1. Init if missing
735
+ if not (self.repo_path / ".git").exists():
736
+ subprocess.run(["git", "init"], cwd=self.repo_path, check=True)
737
+ subprocess.run(["git", "remote", "add", "origin", remote_url], cwd=self.repo_path)
738
+
739
+ # 2. Fetch and Reset
740
+ subprocess.run(["git", "fetch", "origin"], cwd=self.repo_path, check=True)
741
+ res = subprocess.run(
742
+ ["git", "reset", "--hard", f"origin/{branch}"],
743
+ cwd=self.repo_path, capture_output=True, text=True
744
+ )
745
+
746
+ if res.returncode == 0:
747
+ return f"βœ… RESTORE COMPLETED. Local files replaced with GitHub/{branch}."
748
+ else:
749
+ return f"⚠️ Pull Failed: {res.stderr}"
750
+
751
+ except Exception as e:
752
+ return f"❌ Critical Git Error: {e}"
753
+
754
+ # =====================================================================
755
+ # WORKING MEMORY NOTEBOOK (Structured List)
756
+ # =====================================================================
757
+ def _load_notebook(self) -> List[Dict]:
758
+ """Internal helper to load notebook JSON."""
759
+ notebook_path = self.repo_path / "memory" / "notebook.json"
760
+ if not notebook_path.exists(): return []
761
+ try: return json.loads(notebook_path.read_text(encoding='utf-8'))
762
+ except: return []
763
+
764
+ def _save_notebook(self, notes: List[Dict]):
765
+ """Internal helper to save notebook JSON with audit logging."""
766
+ notebook_path = self.repo_path / "memory" / "notebook.json"
767
+ log_path = self.repo_path / "memory" / "history.log"
768
+ notebook_path.parent.mkdir(parents=True, exist_ok=True)
769
+ notebook_path.write_text(json.dumps(notes, indent=2), encoding='utf-8')
770
+ try:
771
+ timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
772
+ with open(log_path, "a") as f: f.write(f"[{timestamp}] NOTEBOOK UPDATED | Count: {len(notes)}\n")
773
+ except: pass
774
+
775
+ def notebook_read(self) -> str:
776
+ """Reads the Working Memory notebook."""
777
+ notes = self._load_notebook()
778
+ if not notes: return ""
779
+ display = []
780
+ for i, note in enumerate(notes):
781
+ display.append(f"{i+1}. [{note['timestamp']}] {note['content']}")
782
+ return "\n".join(display)
783
+
784
+ def notebook_add(self, content: str) -> str:
785
+ """Adds a new note to the notebook."""
786
+ notes = self._load_notebook()
787
+ if len(notes) >= 25: return "⚠️ Notebook full (25/25 slots). Please delete obsolete notes first."
788
+ timestamp = time.strftime("%Y-%m-%d %H:%M")
789
+ notes.append({"timestamp": timestamp, "content": content})
790
+ self._save_notebook(notes)
791
+ return f"βœ… Note added. ({len(notes)}/25 slots used)"
792
+
793
+ def notebook_delete(self, index: int) -> str:
794
+ """Deletes a note by its number (1-based index)."""
795
+ notes = self._load_notebook()
796
+ try:
797
+ idx = int(index) - 1
798
+ if 0 <= idx < len(notes):
799
+ removed = notes.pop(idx)
800
+ self._save_notebook(notes)
801
+ return f"βœ… Deleted note #{index}: '{removed['content'][:30]}...'"
802
+ else: return f"❌ Invalid index: {index}. Valid range: 1-{len(notes)}"
803
+ except ValueError: return "❌ Index must be a number."
804
+