up-cli 0.1.1__py3-none-any.whl → 0.5.0__py3-none-any.whl

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.
Files changed (55) hide show
  1. up/__init__.py +1 -1
  2. up/ai_cli.py +229 -0
  3. up/cli.py +75 -4
  4. up/commands/agent.py +521 -0
  5. up/commands/bisect.py +343 -0
  6. up/commands/branch.py +350 -0
  7. up/commands/dashboard.py +248 -0
  8. up/commands/init.py +195 -6
  9. up/commands/learn.py +1741 -0
  10. up/commands/memory.py +545 -0
  11. up/commands/new.py +108 -10
  12. up/commands/provenance.py +267 -0
  13. up/commands/review.py +239 -0
  14. up/commands/start.py +1124 -0
  15. up/commands/status.py +360 -0
  16. up/commands/summarize.py +122 -0
  17. up/commands/sync.py +317 -0
  18. up/commands/vibe.py +304 -0
  19. up/context.py +421 -0
  20. up/core/__init__.py +69 -0
  21. up/core/checkpoint.py +479 -0
  22. up/core/provenance.py +364 -0
  23. up/core/state.py +678 -0
  24. up/events.py +512 -0
  25. up/git/__init__.py +37 -0
  26. up/git/utils.py +270 -0
  27. up/git/worktree.py +331 -0
  28. up/learn/__init__.py +155 -0
  29. up/learn/analyzer.py +227 -0
  30. up/learn/plan.py +374 -0
  31. up/learn/research.py +511 -0
  32. up/learn/utils.py +117 -0
  33. up/memory.py +1096 -0
  34. up/parallel.py +551 -0
  35. up/summarizer.py +407 -0
  36. up/templates/__init__.py +70 -2
  37. up/templates/config/__init__.py +502 -20
  38. up/templates/docs/SKILL.md +28 -0
  39. up/templates/docs/__init__.py +341 -0
  40. up/templates/docs/standards/HEADERS.md +24 -0
  41. up/templates/docs/standards/STRUCTURE.md +18 -0
  42. up/templates/docs/standards/TEMPLATES.md +19 -0
  43. up/templates/learn/__init__.py +567 -14
  44. up/templates/loop/__init__.py +546 -27
  45. up/templates/mcp/__init__.py +474 -0
  46. up/templates/projects/__init__.py +786 -0
  47. up/ui/__init__.py +14 -0
  48. up/ui/loop_display.py +650 -0
  49. up/ui/theme.py +137 -0
  50. up_cli-0.5.0.dist-info/METADATA +519 -0
  51. up_cli-0.5.0.dist-info/RECORD +55 -0
  52. up_cli-0.1.1.dist-info/METADATA +0 -186
  53. up_cli-0.1.1.dist-info/RECORD +0 -14
  54. {up_cli-0.1.1.dist-info → up_cli-0.5.0.dist-info}/WHEEL +0 -0
  55. {up_cli-0.1.1.dist-info → up_cli-0.5.0.dist-info}/entry_points.txt +0 -0
up/learn/__init__.py ADDED
@@ -0,0 +1,155 @@
1
+ """Learning system CLI commands.
2
+
3
+ This module provides the main CLI entry points for the learning system.
4
+ The implementation is split across several submodules:
5
+ - utils: Shared utilities
6
+ - analyzer: Project analysis
7
+ - research: Topic and file learning
8
+ - plan: PRD generation
9
+ """
10
+
11
+ from pathlib import Path
12
+
13
+ import click
14
+ from rich.console import Console
15
+ from rich.panel import Panel
16
+
17
+ from up.learn.utils import check_vision_map_exists, is_valid_path, find_skill_dir, display_profile, save_profile
18
+ from up.learn.analyzer import analyze_project, learn_self_improvement
19
+ from up.learn.research import learn_from_topic, learn_from_file, learn_from_project
20
+ from up.learn.plan import learn_analyze, learn_plan, learn_status
21
+
22
+ console = Console()
23
+
24
+
25
+ @click.group(invoke_without_command=True)
26
+ @click.argument("topic_or_path", required=False)
27
+ @click.option("--workspace", "-w", type=click.Path(exists=True), help="Workspace path")
28
+ @click.option("--no-ai", is_flag=True, help="Disable AI analysis")
29
+ @click.pass_context
30
+ def learn_cmd(ctx, topic_or_path: str, workspace: str, no_ai: bool):
31
+ """Learning system - analyze, research, and improve.
32
+
33
+ \b
34
+ Usage:
35
+ up learn Auto-analyze and improve
36
+ up learn "topic" Learn about a specific topic
37
+ up learn "file.md" Analyze file with AI
38
+ up learn "project/path" Compare and learn from another project
39
+
40
+ \b
41
+ Subcommands:
42
+ up learn auto Analyze project (no vision check)
43
+ up learn analyze Analyze all research files
44
+ up learn plan Generate improvement PRD
45
+ up learn status Show learning system status
46
+ """
47
+ if ctx.invoked_subcommand is not None:
48
+ return
49
+
50
+ ctx.ensure_object(dict)
51
+ ctx.obj['workspace'] = workspace
52
+ ctx.obj['no_ai'] = no_ai
53
+
54
+ # Check if topic_or_path is a subcommand
55
+ subcommands = ["auto", "analyze", "plan", "status"]
56
+ if topic_or_path in subcommands:
57
+ subcmd = ctx.command.commands[topic_or_path]
58
+ ctx.invoke(subcmd, workspace=workspace)
59
+ return
60
+
61
+ ws = Path(workspace) if workspace else Path.cwd()
62
+ use_ai = not no_ai
63
+
64
+ # No argument: self-improvement mode
65
+ if not topic_or_path:
66
+ vision_exists, vision_path = check_vision_map_exists(ws)
67
+
68
+ if not vision_exists:
69
+ console.print(Panel.fit(
70
+ "[yellow]Vision Map Not Configured[/]",
71
+ border_style="yellow"
72
+ ))
73
+ console.print("\nThe learning system requires a configured vision map.")
74
+ console.print(f"\nPlease configure: [cyan]{vision_path}[/]")
75
+ console.print("\n[bold]Alternatives:[/]")
76
+ console.print(" • [cyan]up learn auto[/] - Analyze without vision map")
77
+ console.print(" • [cyan]up learn \"topic\"[/] - Learn about specific topic")
78
+ return
79
+
80
+ learn_self_improvement(ws, use_ai=use_ai)
81
+ return
82
+
83
+ # Has argument: determine if topic or path
84
+ if is_valid_path(topic_or_path):
85
+ learn_from_project(ws, topic_or_path, use_ai=use_ai)
86
+ else:
87
+ learn_from_topic(ws, topic_or_path, use_ai=use_ai)
88
+
89
+
90
+ @learn_cmd.command("auto")
91
+ @click.option("--workspace", "-w", type=click.Path(exists=True), help="Workspace path")
92
+ def auto_cmd(workspace: str):
93
+ """Auto-analyze project and identify improvements."""
94
+ ws = Path(workspace) if workspace else Path.cwd()
95
+
96
+ console.print(Panel.fit(
97
+ "[bold blue]Learning System[/] - Auto Analysis",
98
+ border_style="blue"
99
+ ))
100
+
101
+ profile = analyze_project(ws)
102
+
103
+ if profile is None:
104
+ console.print("[red]Error: Could not analyze project[/]")
105
+ return
106
+
107
+ display_profile(profile)
108
+
109
+ save_path = save_profile(ws, profile)
110
+ console.print(f"\n[green]✓[/] Profile saved to: [cyan]{save_path}[/]")
111
+
112
+ console.print("\n[bold]Next Steps:[/]")
113
+ if profile.get("research_topics"):
114
+ console.print(" 1. Research topics with: [cyan]up learn \"topic\"[/]")
115
+ console.print(" 2. Generate PRD with: [cyan]up learn plan[/]")
116
+ console.print(" 3. Start development with: [cyan]up start[/]")
117
+
118
+
119
+ @learn_cmd.command("analyze")
120
+ @click.option("--workspace", "-w", type=click.Path(exists=True), help="Workspace path")
121
+ def analyze_cmd(workspace: str):
122
+ """Analyze all research files with AI."""
123
+ ws = Path(workspace) if workspace else Path.cwd()
124
+ learn_analyze(ws)
125
+
126
+
127
+ @learn_cmd.command("plan")
128
+ @click.option("--workspace", "-w", type=click.Path(exists=True), help="Workspace path")
129
+ @click.option("--output", "-o", type=click.Path(), help="Output file path")
130
+ def plan_cmd(workspace: str, output: str):
131
+ """Generate improvement PRD from analysis."""
132
+ ws = Path(workspace) if workspace else Path.cwd()
133
+ learn_plan(ws, output)
134
+
135
+
136
+ @learn_cmd.command("status")
137
+ @click.option("--workspace", "-w", type=click.Path(exists=True), help="Workspace path")
138
+ def status_cmd(workspace: str):
139
+ """Show learning system status."""
140
+ ws = Path(workspace) if workspace else Path.cwd()
141
+ learn_status(ws)
142
+
143
+
144
+ # Export for external use
145
+ __all__ = [
146
+ "learn_cmd",
147
+ "analyze_project",
148
+ "learn_self_improvement",
149
+ "learn_from_topic",
150
+ "learn_from_file",
151
+ "learn_from_project",
152
+ "learn_analyze",
153
+ "learn_plan",
154
+ "learn_status",
155
+ ]
up/learn/analyzer.py ADDED
@@ -0,0 +1,227 @@
1
+ """Project analysis for the learning system."""
2
+
3
+ import os
4
+ import re
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ from rich.console import Console
9
+
10
+ from up.learn.utils import find_skill_dir, display_profile, save_profile, load_profile, record_to_memory
11
+
12
+ console = Console()
13
+
14
+
15
+ def analyze_project(workspace: Path) -> dict:
16
+ """Analyze project and return profile."""
17
+ profile = {
18
+ "name": workspace.name,
19
+ "languages": [],
20
+ "frameworks": [],
21
+ "patterns_detected": [],
22
+ "improvement_areas": [],
23
+ "research_topics": [],
24
+ }
25
+
26
+ # Extension to language mapping
27
+ extensions = {
28
+ ".py": "Python",
29
+ ".js": "JavaScript",
30
+ ".ts": "TypeScript",
31
+ ".tsx": "TypeScript",
32
+ ".go": "Go",
33
+ ".rs": "Rust",
34
+ ".java": "Java",
35
+ ".rb": "Ruby",
36
+ }
37
+
38
+ # Framework indicators
39
+ framework_indicators = {
40
+ "fastapi": "FastAPI",
41
+ "django": "Django",
42
+ "flask": "Flask",
43
+ "react": "React",
44
+ "next": "Next.js",
45
+ "vue": "Vue.js",
46
+ "langchain": "LangChain",
47
+ "langgraph": "LangGraph",
48
+ "express": "Express",
49
+ "pytest": "pytest",
50
+ }
51
+
52
+ # Detect languages
53
+ skip_dirs = {".git", "node_modules", "__pycache__", ".venv", "venv", "build", "dist"}
54
+ found_languages = set()
55
+
56
+ for root, dirs, files in os.walk(workspace):
57
+ dirs[:] = [d for d in dirs if d not in skip_dirs]
58
+ for f in files:
59
+ ext = Path(f).suffix.lower()
60
+ if ext in extensions:
61
+ found_languages.add(extensions[ext])
62
+
63
+ profile["languages"] = sorted(found_languages)
64
+
65
+ # Detect frameworks
66
+ config_files = [
67
+ workspace / "pyproject.toml",
68
+ workspace / "requirements.txt",
69
+ workspace / "package.json",
70
+ ]
71
+
72
+ found_frameworks = set()
73
+ for config in config_files:
74
+ if config.exists():
75
+ try:
76
+ content = config.read_text().lower()
77
+ for key, name in framework_indicators.items():
78
+ if key in content:
79
+ found_frameworks.add(name)
80
+ except Exception:
81
+ pass
82
+
83
+ profile["frameworks"] = sorted(found_frameworks)
84
+
85
+ # Detect patterns
86
+ pattern_indicators = {
87
+ r"class.*Repository": "Repository Pattern",
88
+ r"class.*Service": "Service Layer",
89
+ r"@dataclass": "Dataclasses",
90
+ r"async def": "Async/Await",
91
+ r"def test_": "Unit Tests",
92
+ r"Protocol\)": "Protocol Pattern",
93
+ }
94
+
95
+ src_dir = workspace / "src"
96
+ if not src_dir.exists():
97
+ src_dir = workspace
98
+
99
+ found_patterns = set()
100
+ for py_file in src_dir.rglob("*.py"):
101
+ try:
102
+ content = py_file.read_text()
103
+ for pattern, name in pattern_indicators.items():
104
+ if re.search(pattern, content, re.IGNORECASE):
105
+ found_patterns.add(name)
106
+ except Exception:
107
+ continue
108
+
109
+ profile["patterns_detected"] = sorted(found_patterns)
110
+
111
+ # Identify improvements
112
+ improvements = []
113
+ if "Python" in profile["languages"]:
114
+ if "Unit Tests" not in profile["patterns_detected"]:
115
+ improvements.append("add-unit-tests")
116
+ if "Protocol Pattern" not in profile["patterns_detected"]:
117
+ improvements.append("add-interfaces")
118
+
119
+ if any(f in profile["frameworks"] for f in ["FastAPI", "Django", "Flask"]):
120
+ improvements.append("add-caching")
121
+
122
+ profile["improvement_areas"] = improvements
123
+
124
+ # Generate research topics
125
+ topic_map = {
126
+ "add-unit-tests": "testing best practices",
127
+ "add-interfaces": "Python Protocol patterns",
128
+ "add-caching": "caching strategies",
129
+ }
130
+
131
+ topics = [topic_map[i] for i in improvements if i in topic_map]
132
+ for fw in profile["frameworks"][:2]:
133
+ topics.append(f"{fw} best practices")
134
+
135
+ profile["research_topics"] = topics[:5]
136
+
137
+ return profile
138
+
139
+
140
+ def analyze_project_structure(project_path: Path) -> list:
141
+ """Analyze project directory structure for insights."""
142
+ insights = []
143
+
144
+ good_patterns = {
145
+ "src": "Source code organization in src/ directory",
146
+ "tests": "Dedicated tests/ directory",
147
+ "docs": "Documentation directory present",
148
+ ".github": "GitHub workflows/CI present",
149
+ "scripts": "Automation scripts directory",
150
+ "__init__.py": "Proper Python package structure",
151
+ "pyproject.toml": "Modern Python packaging (PEP 517)",
152
+ "Makefile": "Make-based automation",
153
+ "docker-compose": "Docker containerization",
154
+ }
155
+
156
+ for pattern, description in good_patterns.items():
157
+ if (project_path / pattern).exists() or any(project_path.glob(f"**/{pattern}")):
158
+ insights.append(description)
159
+
160
+ return insights[:5]
161
+
162
+
163
+ def learn_self_improvement(workspace: Path, use_ai: bool = True) -> dict:
164
+ """Analyze current project for self-improvement opportunities."""
165
+ from rich.panel import Panel
166
+
167
+ console.print(Panel.fit(
168
+ "[bold blue]Learning System[/] - Self-Improvement Analysis",
169
+ border_style="blue"
170
+ ))
171
+
172
+ profile = analyze_project(workspace)
173
+ if not profile:
174
+ return {}
175
+
176
+ # Load existing profile to track improvements
177
+ old_profile = load_profile(workspace)
178
+
179
+ # Identify what changed
180
+ improvements = {
181
+ "new_patterns": [],
182
+ "new_frameworks": [],
183
+ "addressed_improvements": [],
184
+ "remaining_improvements": [],
185
+ }
186
+
187
+ old_patterns = set(old_profile.get("patterns_detected", []))
188
+ new_patterns = set(profile.get("patterns_detected", []))
189
+ improvements["new_patterns"] = list(new_patterns - old_patterns)
190
+
191
+ old_frameworks = set(old_profile.get("frameworks", []))
192
+ new_frameworks = set(profile.get("frameworks", []))
193
+ improvements["new_frameworks"] = list(new_frameworks - old_frameworks)
194
+
195
+ old_areas = set(old_profile.get("improvement_areas", []))
196
+ new_areas = set(profile.get("improvement_areas", []))
197
+ improvements["addressed_improvements"] = list(old_areas - new_areas)
198
+ improvements["remaining_improvements"] = list(new_areas)
199
+
200
+ # Display results
201
+ display_profile(profile)
202
+
203
+ if improvements["new_patterns"]:
204
+ console.print("\n[green]✓ New Patterns Adopted:[/]")
205
+ for p in improvements["new_patterns"]:
206
+ console.print(f" • {p}")
207
+
208
+ if improvements["addressed_improvements"]:
209
+ console.print("\n[green]✓ Improvements Addressed:[/]")
210
+ for a in improvements["addressed_improvements"]:
211
+ console.print(f" • {a}")
212
+
213
+ if improvements["remaining_improvements"]:
214
+ console.print("\n[yellow]○ Areas for Improvement:[/]")
215
+ for r in improvements["remaining_improvements"]:
216
+ console.print(f" • {r}")
217
+
218
+ # Save updated profile
219
+ save_path = save_profile(workspace, profile)
220
+ console.print(f"\n[green]✓[/] Profile updated: [cyan]{save_path}[/]")
221
+
222
+ # Record to memory
223
+ content = f"Self-improvement analysis: Found {len(profile.get('patterns_detected', []))} patterns, "
224
+ content += f"{len(improvements.get('new_patterns', []))} new patterns adopted"
225
+ record_to_memory(workspace, content)
226
+
227
+ return improvements