ragtime-cli 0.1.0__tar.gz → 0.1.2__tar.gz
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.
Potentially problematic release.
This version of ragtime-cli might be problematic. Click here for more details.
- {ragtime_cli-0.1.0/ragtime_cli.egg-info → ragtime_cli-0.1.2}/PKG-INFO +1 -1
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/pyproject.toml +1 -1
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2/ragtime_cli.egg-info}/PKG-INFO +1 -1
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/cli.py +286 -1
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/LICENSE +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/README.md +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/ragtime_cli.egg-info/SOURCES.txt +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/ragtime_cli.egg-info/dependency_links.txt +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/ragtime_cli.egg-info/entry_points.txt +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/ragtime_cli.egg-info/requires.txt +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/ragtime_cli.egg-info/top_level.txt +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/setup.cfg +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/__init__.py +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/commands/audit.md +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/commands/handoff.md +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/commands/pr-graduate.md +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/commands/recall.md +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/commands/remember.md +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/commands/save.md +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/commands/start.md +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/config.py +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/db.py +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/indexers/__init__.py +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/indexers/docs.py +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/mcp_server.py +0 -0
- {ragtime_cli-0.1.0 → ragtime_cli-0.1.2}/src/memory.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ragtime-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Local-first memory and RAG system for Claude Code - semantic search over code, docs, and team knowledge
|
|
5
5
|
Author-email: Bret Martineau <bretwardjames@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ragtime-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Local-first memory and RAG system for Claude Code - semantic search over code, docs, and team knowledge
|
|
5
5
|
Author-email: Bret Martineau <bretwardjames@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -55,8 +55,75 @@ def get_author() -> str:
|
|
|
55
55
|
return "unknown"
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
def check_ghp_installed() -> bool:
|
|
59
|
+
"""Check if ghp-cli is installed."""
|
|
60
|
+
try:
|
|
61
|
+
result = subprocess.run(
|
|
62
|
+
["ghp", "--version"],
|
|
63
|
+
capture_output=True,
|
|
64
|
+
text=True,
|
|
65
|
+
timeout=5,
|
|
66
|
+
)
|
|
67
|
+
return result.returncode == 0
|
|
68
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_issue_from_ghp(issue_num: int, path: Path) -> dict | None:
|
|
73
|
+
"""Get issue details using ghp issue open."""
|
|
74
|
+
try:
|
|
75
|
+
result = subprocess.run(
|
|
76
|
+
["ghp", "issue", "open", str(issue_num), "--json"],
|
|
77
|
+
cwd=path,
|
|
78
|
+
capture_output=True,
|
|
79
|
+
text=True,
|
|
80
|
+
timeout=30,
|
|
81
|
+
)
|
|
82
|
+
if result.returncode == 0:
|
|
83
|
+
import json
|
|
84
|
+
return json.loads(result.stdout)
|
|
85
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, Exception):
|
|
86
|
+
pass
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_issue_from_gh(issue_num: int, path: Path) -> dict | None:
|
|
91
|
+
"""Get issue details using gh CLI."""
|
|
92
|
+
try:
|
|
93
|
+
result = subprocess.run(
|
|
94
|
+
["gh", "issue", "view", str(issue_num), "--json", "title,body,labels,number"],
|
|
95
|
+
cwd=path,
|
|
96
|
+
capture_output=True,
|
|
97
|
+
text=True,
|
|
98
|
+
timeout=30,
|
|
99
|
+
)
|
|
100
|
+
if result.returncode == 0:
|
|
101
|
+
import json
|
|
102
|
+
return json.loads(result.stdout)
|
|
103
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, Exception):
|
|
104
|
+
pass
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_current_branch(path: Path) -> str | None:
|
|
109
|
+
"""Get the current git branch name."""
|
|
110
|
+
try:
|
|
111
|
+
result = subprocess.run(
|
|
112
|
+
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
113
|
+
cwd=path,
|
|
114
|
+
capture_output=True,
|
|
115
|
+
text=True,
|
|
116
|
+
timeout=5,
|
|
117
|
+
)
|
|
118
|
+
if result.returncode == 0:
|
|
119
|
+
return result.stdout.strip()
|
|
120
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
121
|
+
pass
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
|
|
58
125
|
@click.group()
|
|
59
|
-
@click.version_option(version="0.1.
|
|
126
|
+
@click.version_option(version="0.1.2")
|
|
60
127
|
def main():
|
|
61
128
|
"""Ragtime - semantic search over code and documentation."""
|
|
62
129
|
pass
|
|
@@ -73,6 +140,20 @@ def init(path: Path):
|
|
|
73
140
|
click.echo(f" Code paths: {config.code.paths}")
|
|
74
141
|
click.echo(f" Languages: {config.code.languages}")
|
|
75
142
|
|
|
143
|
+
# Create memory directory structure
|
|
144
|
+
memory_dir = path / ".claude" / "memory"
|
|
145
|
+
(memory_dir / "app").mkdir(parents=True, exist_ok=True)
|
|
146
|
+
(memory_dir / "team").mkdir(parents=True, exist_ok=True)
|
|
147
|
+
(memory_dir / "branches").mkdir(parents=True, exist_ok=True)
|
|
148
|
+
click.echo(f"\nCreated .claude/memory/ structure")
|
|
149
|
+
|
|
150
|
+
# Check for ghp-cli
|
|
151
|
+
if check_ghp_installed():
|
|
152
|
+
click.echo(f"\n✓ ghp-cli detected - will use for issue lookups")
|
|
153
|
+
else:
|
|
154
|
+
click.echo(f"\n• ghp-cli not found - will use gh CLI for issue lookups")
|
|
155
|
+
click.echo(f" Install ghp for enhanced workflow: pip install ghp-cli")
|
|
156
|
+
|
|
76
157
|
|
|
77
158
|
@main.command()
|
|
78
159
|
@click.argument("path", type=click.Path(exists=True, path_type=Path), default=".")
|
|
@@ -415,6 +496,141 @@ def reindex(path: Path):
|
|
|
415
496
|
click.echo(f"✓ Reindexed {count} memory files")
|
|
416
497
|
|
|
417
498
|
|
|
499
|
+
@main.command("new-branch")
|
|
500
|
+
@click.argument("issue", type=int)
|
|
501
|
+
@click.option("--path", type=click.Path(exists=True, path_type=Path), default=".")
|
|
502
|
+
@click.option("--content", help="Context document content (overrides auto-generated scaffold)")
|
|
503
|
+
@click.option("--issue-json", "issue_json", help="Issue data as JSON (from ghp hook, skips fetch)")
|
|
504
|
+
@click.option("--branch", "-b", help="Branch name (auto-detected from git if not provided)")
|
|
505
|
+
def new_branch(issue: int, path: Path, content: str, issue_json: str, branch: str):
|
|
506
|
+
"""Initialize a branch context from a GitHub issue.
|
|
507
|
+
|
|
508
|
+
Creates .claude/memory/branches/{branch-slug}/context.md with either:
|
|
509
|
+
- Provided content (from --content flag, e.g., LLM-generated plan)
|
|
510
|
+
- Auto-generated scaffold from issue metadata (fallback)
|
|
511
|
+
|
|
512
|
+
Examples:
|
|
513
|
+
ragtime new-branch 42 # Scaffold from issue #42
|
|
514
|
+
ragtime new-branch 42 --content "..." # Use provided content
|
|
515
|
+
ragtime new-branch 42 --issue-json '{...}' # Use JSON from ghp hook
|
|
516
|
+
ragtime new-branch 42 --branch my-feat # Specify branch name
|
|
517
|
+
"""
|
|
518
|
+
import json
|
|
519
|
+
from datetime import date
|
|
520
|
+
|
|
521
|
+
path = Path(path).resolve()
|
|
522
|
+
|
|
523
|
+
# Determine branch name
|
|
524
|
+
if not branch:
|
|
525
|
+
branch = get_current_branch(path)
|
|
526
|
+
if not branch or branch in ("main", "master"):
|
|
527
|
+
click.echo("✗ Not on a feature branch. Use --branch to specify.", err=True)
|
|
528
|
+
return
|
|
529
|
+
|
|
530
|
+
# Create branch slug for folder name
|
|
531
|
+
branch_slug = branch.replace("/", "-")
|
|
532
|
+
branch_dir = path / ".claude" / "memory" / "branches" / branch_slug
|
|
533
|
+
branch_dir.mkdir(parents=True, exist_ok=True)
|
|
534
|
+
|
|
535
|
+
context_file = branch_dir / "context.md"
|
|
536
|
+
|
|
537
|
+
if content:
|
|
538
|
+
# Use provided content directly
|
|
539
|
+
context_file.write_text(content)
|
|
540
|
+
click.echo(f"✓ Created context.md with provided content")
|
|
541
|
+
click.echo(f" Path: {context_file.relative_to(path)}")
|
|
542
|
+
return
|
|
543
|
+
|
|
544
|
+
# Get issue data from JSON, ghp, or gh
|
|
545
|
+
issue_data = None
|
|
546
|
+
source = None
|
|
547
|
+
|
|
548
|
+
# Use provided JSON if available (from ghp hook)
|
|
549
|
+
if issue_json:
|
|
550
|
+
try:
|
|
551
|
+
issue_data = json.loads(issue_json)
|
|
552
|
+
source = "ghp-hook"
|
|
553
|
+
except json.JSONDecodeError as e:
|
|
554
|
+
click.echo(f"✗ Invalid JSON: {e}", err=True)
|
|
555
|
+
return
|
|
556
|
+
else:
|
|
557
|
+
# Fall back to fetching from API
|
|
558
|
+
click.echo(f"Fetching issue #{issue}...")
|
|
559
|
+
|
|
560
|
+
# Try ghp first if available
|
|
561
|
+
if check_ghp_installed():
|
|
562
|
+
issue_data = get_issue_from_ghp(issue, path)
|
|
563
|
+
source = "ghp"
|
|
564
|
+
|
|
565
|
+
# Fall back to gh
|
|
566
|
+
if not issue_data:
|
|
567
|
+
issue_data = get_issue_from_gh(issue, path)
|
|
568
|
+
source = "gh"
|
|
569
|
+
|
|
570
|
+
if not issue_data:
|
|
571
|
+
click.echo(f"✗ Could not fetch issue #{issue}", err=True)
|
|
572
|
+
click.echo(" Make sure you're in a git repo with GitHub remote")
|
|
573
|
+
return
|
|
574
|
+
|
|
575
|
+
# Extract issue fields
|
|
576
|
+
title = issue_data.get("title", f"Issue #{issue}")
|
|
577
|
+
body = issue_data.get("body", "")
|
|
578
|
+
labels = issue_data.get("labels", [])
|
|
579
|
+
|
|
580
|
+
# Format labels
|
|
581
|
+
if labels:
|
|
582
|
+
if isinstance(labels[0], dict):
|
|
583
|
+
label_names = [l.get("name", "") for l in labels]
|
|
584
|
+
else:
|
|
585
|
+
label_names = labels
|
|
586
|
+
labels_str = ", ".join(label_names)
|
|
587
|
+
else:
|
|
588
|
+
labels_str = ""
|
|
589
|
+
|
|
590
|
+
# Generate scaffold context.md
|
|
591
|
+
scaffold = f"""---
|
|
592
|
+
type: context
|
|
593
|
+
branch: {branch}
|
|
594
|
+
issue: {issue}
|
|
595
|
+
status: active
|
|
596
|
+
created: '{date.today().isoformat()}'
|
|
597
|
+
author: {get_author()}
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
## Issue
|
|
601
|
+
|
|
602
|
+
**#{issue}**: {title}
|
|
603
|
+
|
|
604
|
+
{f"**Labels**: {labels_str}" if labels_str else ""}
|
|
605
|
+
|
|
606
|
+
## Description
|
|
607
|
+
|
|
608
|
+
{body if body else "_No description provided_"}
|
|
609
|
+
|
|
610
|
+
## Plan
|
|
611
|
+
|
|
612
|
+
<!-- Implementation steps - fill in or let Claude generate -->
|
|
613
|
+
|
|
614
|
+
- [ ] TODO: Define implementation steps
|
|
615
|
+
|
|
616
|
+
## Acceptance Criteria
|
|
617
|
+
|
|
618
|
+
<!-- What needs to be true for this to be complete? -->
|
|
619
|
+
|
|
620
|
+
## Notes
|
|
621
|
+
|
|
622
|
+
<!-- Additional context, decisions, blockers -->
|
|
623
|
+
|
|
624
|
+
"""
|
|
625
|
+
|
|
626
|
+
context_file.write_text(scaffold)
|
|
627
|
+
|
|
628
|
+
click.echo(f"✓ Created context.md from issue #{issue}")
|
|
629
|
+
click.echo(f" Path: {context_file.relative_to(path)}")
|
|
630
|
+
click.echo(f" Source: {source}")
|
|
631
|
+
click.echo(f"\nNext: Fill in the Plan section or use /start to generate it")
|
|
632
|
+
|
|
633
|
+
|
|
418
634
|
# ============================================================================
|
|
419
635
|
# Command Installation
|
|
420
636
|
# ============================================================================
|
|
@@ -518,6 +734,75 @@ def install_commands(global_install: bool, workspace_install: bool, list_command
|
|
|
518
734
|
click.echo(' "ragtime": {"command": "ragtime-mcp", "args": ["--path", "."]}')
|
|
519
735
|
|
|
520
736
|
|
|
737
|
+
@main.command("setup-ghp")
|
|
738
|
+
@click.option("--remove", is_flag=True, help="Remove ragtime hooks from ghp")
|
|
739
|
+
def setup_ghp(remove: bool):
|
|
740
|
+
"""Register ragtime hooks with ghp-cli.
|
|
741
|
+
|
|
742
|
+
Adds event hooks so ghp automatically creates context.md when starting issues.
|
|
743
|
+
|
|
744
|
+
Examples:
|
|
745
|
+
ragtime setup-ghp # Register hooks
|
|
746
|
+
ragtime setup-ghp --remove # Remove hooks
|
|
747
|
+
"""
|
|
748
|
+
if not check_ghp_installed():
|
|
749
|
+
click.echo("✗ ghp-cli not installed", err=True)
|
|
750
|
+
click.echo(" Install with: npm install -g @bretwardjames/ghp-cli")
|
|
751
|
+
return
|
|
752
|
+
|
|
753
|
+
hook_name = "ragtime-context"
|
|
754
|
+
|
|
755
|
+
if remove:
|
|
756
|
+
# Remove the hook
|
|
757
|
+
result = subprocess.run(
|
|
758
|
+
["ghp", "hooks", "remove", hook_name],
|
|
759
|
+
capture_output=True,
|
|
760
|
+
text=True,
|
|
761
|
+
)
|
|
762
|
+
if result.returncode == 0:
|
|
763
|
+
click.echo(f"✓ Removed hook: {hook_name}")
|
|
764
|
+
else:
|
|
765
|
+
if "not found" in result.stderr.lower():
|
|
766
|
+
click.echo(f"• Hook {hook_name} not registered")
|
|
767
|
+
else:
|
|
768
|
+
click.echo(f"✗ Failed to remove hook: {result.stderr}", err=True)
|
|
769
|
+
return
|
|
770
|
+
|
|
771
|
+
# Check if hook already exists
|
|
772
|
+
result = subprocess.run(
|
|
773
|
+
["ghp", "hooks", "show", hook_name],
|
|
774
|
+
capture_output=True,
|
|
775
|
+
text=True,
|
|
776
|
+
)
|
|
777
|
+
if result.returncode == 0:
|
|
778
|
+
click.echo(f"• Hook {hook_name} already registered")
|
|
779
|
+
click.echo(" Use --remove to unregister first")
|
|
780
|
+
return
|
|
781
|
+
|
|
782
|
+
# Register the hook
|
|
783
|
+
# The command uses ${issue.number}, ${issue.json}, and ${branch} from ghp
|
|
784
|
+
hook_command = "ragtime new-branch ${issue.number} --issue-json '${issue.json}' --branch '${branch}'"
|
|
785
|
+
|
|
786
|
+
result = subprocess.run(
|
|
787
|
+
[
|
|
788
|
+
"ghp", "hooks", "add", hook_name,
|
|
789
|
+
"--event", "issue-started",
|
|
790
|
+
"--command", hook_command,
|
|
791
|
+
"--display-name", "Ragtime Context",
|
|
792
|
+
],
|
|
793
|
+
capture_output=True,
|
|
794
|
+
text=True,
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
if result.returncode == 0:
|
|
798
|
+
click.echo(f"✓ Registered hook: {hook_name}")
|
|
799
|
+
click.echo(f" Event: issue-started")
|
|
800
|
+
click.echo(f" Action: Creates context.md from issue metadata")
|
|
801
|
+
click.echo(f"\nNow when you run 'ghp start <issue>', ragtime will auto-create context.md")
|
|
802
|
+
else:
|
|
803
|
+
click.echo(f"✗ Failed to register hook: {result.stderr}", err=True)
|
|
804
|
+
|
|
805
|
+
|
|
521
806
|
# ============================================================================
|
|
522
807
|
# Cross-Branch Sync Commands
|
|
523
808
|
# ============================================================================
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|