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/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """up - AI-powered project scaffolding CLI."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.4.0"
up/ai_cli.py ADDED
@@ -0,0 +1,229 @@
1
+ """AI CLI utilities - shared functions for Claude and Cursor agent CLIs."""
2
+
3
+ import shutil
4
+ import subprocess
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ from rich.console import Console
9
+
10
+ console = Console()
11
+
12
+
13
+ # =============================================================================
14
+ # Exceptions
15
+ # =============================================================================
16
+
17
+ class AICliError(Exception):
18
+ """Base exception for AI CLI operations."""
19
+ pass
20
+
21
+
22
+ class AICliNotFoundError(AICliError):
23
+ """No AI CLI is installed or available."""
24
+ pass
25
+
26
+
27
+ class AICliTimeoutError(AICliError):
28
+ """AI CLI command timed out."""
29
+
30
+ def __init__(self, message: str, timeout: int):
31
+ super().__init__(message)
32
+ self.timeout = timeout
33
+
34
+
35
+ class AICliExecutionError(AICliError):
36
+ """AI CLI command failed to execute."""
37
+
38
+ def __init__(self, message: str, returncode: int, stderr: str = ""):
39
+ super().__init__(message)
40
+ self.returncode = returncode
41
+ self.stderr = stderr
42
+
43
+
44
+ # =============================================================================
45
+ # CLI Detection
46
+ # =============================================================================
47
+
48
+
49
+ def check_ai_cli() -> tuple[str, bool]:
50
+ """Check which AI CLI is available.
51
+
52
+ Checks for Claude CLI first, then Cursor agent CLI.
53
+
54
+ Returns:
55
+ (cli_name, available) - e.g., ("claude", True) or ("agent", True)
56
+ """
57
+ # Check for Claude CLI first
58
+ if shutil.which("claude"):
59
+ return "claude", True
60
+
61
+ # Check for Cursor Agent CLI
62
+ # See: https://cursor.com/docs/cli/overview
63
+ if shutil.which("agent"):
64
+ return "agent", True
65
+
66
+ return "", False
67
+
68
+
69
+ def run_ai_prompt(
70
+ workspace: Path,
71
+ prompt: str,
72
+ cli_name: str,
73
+ timeout: int = 180,
74
+ silent: bool = False
75
+ ) -> Optional[str]:
76
+ """Run a prompt through AI CLI and return the response.
77
+
78
+ Supports both Claude CLI and Cursor agent CLI.
79
+
80
+ Args:
81
+ workspace: Working directory
82
+ prompt: The prompt to send
83
+ cli_name: "claude" or "agent"
84
+ timeout: Timeout in seconds (default 3 minutes)
85
+ silent: If True, don't print warning messages on failure
86
+
87
+ Returns:
88
+ Response text or None if failed
89
+ """
90
+ # Verify CLI is available
91
+ if not shutil.which(cli_name):
92
+ if not silent:
93
+ console.print(f"[yellow]AI CLI '{cli_name}' not found, using basic analysis[/]")
94
+ return None
95
+
96
+ try:
97
+ if cli_name == "claude":
98
+ # Claude CLI: claude -p "prompt"
99
+ cmd = ["claude", "-p", prompt]
100
+ else:
101
+ # Cursor agent CLI: agent -p "prompt" --output-format text
102
+ # See: https://cursor.com/docs/cli/overview
103
+ cmd = ["agent", "-p", prompt, "--output-format", "text"]
104
+
105
+ result = subprocess.run(
106
+ cmd,
107
+ capture_output=True,
108
+ text=True,
109
+ timeout=timeout,
110
+ cwd=workspace
111
+ )
112
+
113
+ if result.returncode == 0 and result.stdout.strip():
114
+ return result.stdout.strip()
115
+
116
+ if not silent and result.stderr:
117
+ console.print(f"[yellow]AI returned error: {result.stderr[:200]}[/]")
118
+ return None
119
+
120
+ except subprocess.TimeoutExpired:
121
+ if not silent:
122
+ console.print(f"[yellow]AI analysis timed out ({timeout}s), using basic analysis[/]")
123
+ return None
124
+ except FileNotFoundError:
125
+ if not silent:
126
+ console.print(f"[yellow]AI CLI '{cli_name}' not found, using basic analysis[/]")
127
+ return None
128
+ except Exception as e:
129
+ if not silent:
130
+ console.print(f"[yellow]AI error: {e}, using basic analysis[/]")
131
+ return None
132
+
133
+
134
+ def run_ai_task(
135
+ workspace: Path,
136
+ prompt: str,
137
+ cli_name: str,
138
+ timeout: int = 600,
139
+ max_tokens: int = 0,
140
+ raise_on_error: bool = False
141
+ ) -> tuple[bool, str]:
142
+ """Run an AI task (like implementing code) and return success status.
143
+
144
+ Args:
145
+ workspace: Working directory
146
+ prompt: The task prompt
147
+ cli_name: "claude" or "agent"
148
+ timeout: Timeout in seconds (default 10 minutes)
149
+ max_tokens: Max output tokens (0 = no limit/use CLI default)
150
+ raise_on_error: If True, raise exceptions instead of returning (False, error)
151
+
152
+ Returns:
153
+ (success, output) tuple
154
+
155
+ Raises:
156
+ AICliNotFoundError: If raise_on_error and CLI not found
157
+ AICliTimeoutError: If raise_on_error and command times out
158
+ AICliExecutionError: If raise_on_error and command fails
159
+ """
160
+ # Verify CLI is available
161
+ if not shutil.which(cli_name):
162
+ error_msg = f"AI CLI '{cli_name}' not found in PATH"
163
+ if raise_on_error:
164
+ raise AICliNotFoundError(error_msg)
165
+ return False, error_msg
166
+
167
+ try:
168
+ if cli_name == "claude":
169
+ # Claude CLI: claude -p "prompt" [--max-tokens N]
170
+ cmd = ["claude", "-p", prompt]
171
+ # Note: Claude CLI doesn't use --max-tokens, it uses model limits
172
+ # The output is effectively unlimited for code tasks
173
+ else:
174
+ # Cursor agent with text output for automation
175
+ cmd = ["agent", "-p", prompt, "--output-format", "text"]
176
+
177
+ result = subprocess.run(
178
+ cmd,
179
+ capture_output=True,
180
+ text=True,
181
+ timeout=timeout,
182
+ cwd=workspace
183
+ )
184
+
185
+ if result.returncode == 0:
186
+ return True, result.stdout
187
+ else:
188
+ stderr = result.stderr or "Unknown error"
189
+ if raise_on_error:
190
+ raise AICliExecutionError(
191
+ f"AI task failed with exit code {result.returncode}",
192
+ returncode=result.returncode,
193
+ stderr=stderr
194
+ )
195
+ return False, stderr
196
+
197
+ except subprocess.TimeoutExpired:
198
+ error_msg = f"AI task timed out ({timeout}s)"
199
+ if raise_on_error:
200
+ raise AICliTimeoutError(error_msg, timeout=timeout)
201
+ return False, error_msg
202
+ except FileNotFoundError:
203
+ # This shouldn't happen since we check with shutil.which, but handle anyway
204
+ error_msg = f"AI CLI '{cli_name}' not found"
205
+ if raise_on_error:
206
+ raise AICliNotFoundError(error_msg)
207
+ return False, error_msg
208
+ except (AICliError,):
209
+ # Re-raise our custom exceptions
210
+ raise
211
+ except Exception as e:
212
+ error_msg = f"Unexpected error: {e}"
213
+ if raise_on_error:
214
+ raise AICliExecutionError(error_msg, returncode=-1, stderr=str(e))
215
+ return False, error_msg
216
+
217
+
218
+ def get_ai_cli_install_instructions() -> str:
219
+ """Get installation instructions for AI CLIs."""
220
+ return """Install one of these AI CLIs:
221
+
222
+ Claude CLI:
223
+ npm install -g @anthropic-ai/claude-code
224
+
225
+ Cursor Agent CLI:
226
+ curl https://cursor.com/install -fsS | bash
227
+
228
+ See: https://cursor.com/docs/cli/overview
229
+ """
up/cli.py CHANGED
@@ -5,23 +5,94 @@ from rich.console import Console
5
5
 
6
6
  from up.commands.init import init_cmd
7
7
  from up.commands.new import new_cmd
8
+ from up.commands.status import status_cmd
9
+ from up.learn import learn_cmd
10
+ from up.commands.summarize import summarize_cmd
11
+ from up.commands.dashboard import dashboard_cmd
12
+ from up.commands.start import start_cmd
13
+ from up.commands.memory import memory_cmd
14
+ from up.commands.sync import sync_cmd, hooks_cmd
15
+ from up.commands.vibe import save_cmd, reset_cmd, diff_cmd
16
+ from up.commands.agent import agent as agent_group
17
+ from up.commands.bisect import bisect_cmd, history_cmd
18
+ from up.commands.provenance import provenance as provenance_group
19
+ from up.commands.review import review_cmd
20
+ from up.commands.branch import branch as branch_group
8
21
 
9
22
  console = Console()
10
23
 
11
24
 
12
25
  @click.group()
13
- @click.version_option(version="0.1.0", prog_name="up")
26
+ @click.version_option(version="0.4.0", prog_name="up")
14
27
  def main():
15
- """up - AI-powered project scaffolding.
28
+ """up - Verifiable, observable AI-assisted development.
16
29
 
17
- Create projects with built-in docs, learn, and product-loop systems
18
- for Claude Code and Cursor AI.
30
+ Build tools using vibe coding with safety rails, resulting in
31
+ stable, high-performance, and modern software engineering.
32
+
33
+ \b
34
+ Quick Start:
35
+ up new my-project Create new project
36
+ up init Initialize in existing project
37
+ up start Start the product loop
38
+ up status Show system health
39
+
40
+ \b
41
+ Vibe Coding Safety Rails:
42
+ up save Checkpoint before AI work
43
+ up reset Restore to checkpoint
44
+ up diff Review AI changes
45
+ up start --parallel Run multiple tasks in parallel
46
+
47
+ \b
48
+ Project Templates:
49
+ up new api --template fastapi FastAPI backend
50
+ up new app --template nextjs Next.js frontend
51
+ up new lib --template python-lib Python library
52
+
53
+ \b
54
+ Memory & Learning:
55
+ up memory search <q> Semantic search
56
+ up memory record Record learnings/decisions
57
+ up learn Auto-improve with AI
58
+
59
+ \b
60
+ System:
61
+ up hooks Install git hooks for auto-sync
62
+ up sync Manual sync all systems
63
+ up dashboard Live monitoring
19
64
  """
20
65
  pass
21
66
 
22
67
 
68
+ # Project commands
23
69
  main.add_command(init_cmd, name="init")
24
70
  main.add_command(new_cmd, name="new")
71
+ main.add_command(status_cmd, name="status")
72
+
73
+ # Vibe coding commands
74
+ main.add_command(start_cmd, name="start")
75
+ main.add_command(save_cmd, name="save")
76
+ main.add_command(reset_cmd, name="reset")
77
+ main.add_command(diff_cmd, name="diff")
78
+ main.add_command(agent_group, name="agent")
79
+
80
+ # Debugging commands
81
+ main.add_command(bisect_cmd, name="bisect")
82
+ main.add_command(history_cmd, name="history")
83
+ main.add_command(provenance_group, name="provenance")
84
+ main.add_command(review_cmd, name="review")
85
+ main.add_command(branch_group, name="branch")
86
+
87
+ # System commands
88
+ main.add_command(sync_cmd, name="sync")
89
+ main.add_command(hooks_cmd, name="hooks")
90
+ main.add_command(dashboard_cmd, name="dashboard")
91
+
92
+ # Learning & memory
93
+ main.add_command(learn_cmd, name="learn")
94
+ main.add_command(memory_cmd, name="memory")
95
+ main.add_command(summarize_cmd, name="summarize")
25
96
 
26
97
 
27
98
  if __name__ == "__main__":