scopemate 0.1.0__py3-none-any.whl → 0.2.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.
scopemate/__init__.py CHANGED
@@ -4,7 +4,7 @@ This package provides tools for breaking down complex tasks using
4
4
  the Purpose/Scope/Outcome planning approach.
5
5
  """
6
6
 
7
- __version__ = "0.1.1"
7
+ __version__ = "0.2.0"
8
8
 
9
9
  # Public API
10
10
  from .models import (
scopemate/breakdown.py CHANGED
@@ -21,11 +21,39 @@ def suggest_breakdown(task: ScopeMateTask) -> List[ScopeMateTask]:
21
21
  """
22
22
  Use LLM to suggest a breakdown of a task into smaller subtasks.
23
23
 
24
+ This function is a critical part of the scopemate workflow. It uses a Large Language
25
+ Model to analyze a task and suggest appropriate subtasks that collectively accomplish
26
+ the parent task's goal. The function handles both complexity-based and time-based
27
+ breakdowns, ensuring that complex tasks are simplified and long-duration tasks are
28
+ broken into manageable timeframes.
29
+
30
+ The function works through these stages:
31
+ 1. Analyze if the breakdown is needed due to complexity or time duration
32
+ 2. Formulate a specialized prompt for the LLM with appropriate constraints
33
+ 3. Process the LLM's response to extract valid subtask definitions
34
+ 4. Convert the raw LLM output into proper ScopeMateTask objects
35
+ 5. Present the suggestions to the user through an interactive selection process
36
+
37
+ The subtasks generated are guaranteed to be:
38
+ - Smaller in scope than the parent task
39
+ - Less complex than the parent task
40
+ - Shorter in duration than the parent task
41
+ - Collectivey covering all aspects needed to accomplish the parent task
42
+
24
43
  Args:
25
- task: The ScopeMateTask to break down
44
+ task: The ScopeMateTask to break down into smaller subtasks
26
45
 
27
46
  Returns:
28
- List of ScopeMateTask objects representing subtasks
47
+ List of ScopeMateTask objects representing the subtasks, after user interaction
48
+
49
+ Example:
50
+ ```python
51
+ parent_task = get_task_by_id("TASK-123")
52
+ subtasks = suggest_breakdown(parent_task)
53
+ if subtasks:
54
+ print(f"Created {len(subtasks)} subtasks")
55
+ tasks.extend(subtasks)
56
+ ```
29
57
  """
30
58
  # Check if we're breaking down due to size complexity or time estimate
31
59
  is_complex = task.scope.size in ["complex", "uncertain", "pioneering"]
scopemate/cli.py CHANGED
@@ -9,12 +9,14 @@ outcome, and output file.
9
9
  import sys
10
10
  import argparse
11
11
  import uuid
12
+ import json
13
+ import os
12
14
  from typing import List
13
15
 
14
16
  from .models import (
15
17
  ScopeMateTask, Purpose, Scope, Outcome, Meta, get_utc_now
16
18
  )
17
- from .storage import save_plan
19
+ from .storage import save_plan, save_markdown_plan, generate_markdown_from_json
18
20
  from .llm import estimate_scope, generate_title_from_purpose_outcome
19
21
  from .breakdown import suggest_breakdown
20
22
  from .task_analysis import check_and_update_parent_estimates
@@ -101,36 +103,76 @@ def process_task_with_breakdown(task: ScopeMateTask) -> List[ScopeMateTask]:
101
103
 
102
104
 
103
105
  def command_line() -> None:
104
- """Process command line arguments and execute appropriate actions."""
106
+ """
107
+ Process command line arguments and execute appropriate actions.
108
+
109
+ This function is the primary entry point for the scopemate CLI, responsible
110
+ for parsing command-line arguments and routing execution to the appropriate
111
+ workflow based on those arguments. It supports two main modes of operation:
112
+
113
+ 1. Interactive mode (--interactive): Launches the full guided workflow with
114
+ the TaskEngine for an interactive task creation and breakdown experience.
115
+
116
+ 2. Non-interactive mode (--purpose and --outcome): Creates a task directly
117
+ from command-line arguments, generates subtasks using LLM, and saves the
118
+ resulting task hierarchy to a JSON file with an automatic Markdown version.
119
+
120
+ The function validates required arguments depending on the mode, provides
121
+ helpful error messages when arguments are missing, and handles the entire
122
+ lifecycle of task creation, breakdown, and saving in non-interactive mode.
123
+
124
+ Command line arguments:
125
+ --interactive: Flag to launch interactive workflow
126
+ --purpose: Text describing why the task matters (required in non-interactive mode)
127
+ --outcome: Text describing what will be delivered (required in non-interactive mode)
128
+ --output: Path to save the output JSON file (default: scopemate_plan.json)
129
+
130
+ Side Effects:
131
+ - Saves task data to a file on disk (both JSON and Markdown versions)
132
+ - Prints progress and error messages to stdout
133
+ - Exits with non-zero status code on errors
134
+
135
+ Example Usage:
136
+ ```bash
137
+ # Interactive mode
138
+ scopemate --interactive
139
+
140
+ # Non-interactive mode
141
+ scopemate --purpose "Improve website performance" \
142
+ --outcome "Page load time under 2 seconds" \
143
+ --output "perf_project.json"
144
+ ```
145
+
146
+ Note: Markdown files are automatically generated with the same base name
147
+ as the JSON file (e.g., "perf_project.md" for "perf_project.json").
148
+ """
105
149
  parser = argparse.ArgumentParser(
106
- description="🪜 scopemate v.0.1.0 - Break down complex projects with LLMs",
107
- epilog="Purpose: why it matters\n"
108
- "Outcome: what will change once it's done\n"
109
- "Scope: how will be delivered (this is where LLM can help)",
150
+ description="🪜 scopemate - Break down complex projects with LLMs",
110
151
  formatter_class=argparse.RawTextHelpFormatter
111
152
  )
112
153
 
113
154
  parser.add_argument(
114
155
  "--interactive",
115
156
  action="store_true",
116
- help="Launch guided workflow to define task, generate LLM-powered breakdowns, and estimate scope"
157
+ help="💡 Interactive mode"
117
158
  )
118
159
 
119
160
  parser.add_argument(
120
161
  "--outcome",
121
- help="🎯 Outcome: Clearly define what will be delivered and how success will be measured (asks: What will change once this is done?)"
162
+ help="🎯 What will change once this is done?"
122
163
  )
123
164
 
124
165
  parser.add_argument(
125
166
  "--purpose",
126
- help="🧭 Purpose: Clearly define why this project matters strategically (asks: Why does this matter now?)"
167
+ help="🧭 Why does this matter now?"
127
168
  )
128
169
 
129
170
  parser.add_argument(
130
171
  "--output",
131
172
  default="scopemate_plan.json",
132
- help="JSON file to save the task breakdown and scope estimates (default: scopemate_plan.json)"
173
+ help="🗂️ (default: scopemate_plan.json)"
133
174
  )
175
+
134
176
  args = parser.parse_args()
135
177
 
136
178
  # Check if running in interactive mode
@@ -154,8 +196,12 @@ def command_line() -> None:
154
196
  print("Generating subtasks...")
155
197
  all_tasks = process_task_with_breakdown(task)
156
198
 
157
- # Save plan to output file
199
+ # Save plan to output file (this will also create the MD version)
158
200
  save_plan(all_tasks, args.output)
201
+
202
+ # Show message about MD file creation
203
+ md_filename = os.path.splitext(args.output)[0] + ".md"
204
+ print(f"✅ Both JSON and Markdown versions have been saved. You can share {md_filename} with team members.")
159
205
 
160
206
 
161
207
  def main():
scopemate/engine.py CHANGED
@@ -25,10 +25,25 @@ from .interaction import prompt_user, build_root_task, print_summary
25
25
  class TaskEngine:
26
26
  """
27
27
  Main engine for scopemate that coordinates task creation, breakdown, and management.
28
+
29
+ The TaskEngine is the central coordinator of the scopemate application, managing the entire
30
+ lifecycle of tasks from creation to breakdown to final output. It handles loading and saving
31
+ task data, organizing the task hierarchy, and orchestrating the breakdown of complex tasks
32
+ into simpler subtasks.
33
+
34
+ Attributes:
35
+ tasks (List[ScopeMateTask]): List of all tasks in the current session
36
+ task_depths (Dict[str, int]): Mapping of task IDs to their depth in the hierarchy
37
+ max_depth (int): Maximum allowed depth for task nesting (default: 5)
28
38
  """
29
39
 
30
40
  def __init__(self):
31
- """Initialize the TaskEngine."""
41
+ """
42
+ Initialize the TaskEngine with empty task list and depth tracking.
43
+
44
+ Creates a new TaskEngine instance with an empty task list and depth tracking dictionary.
45
+ Sets the default maximum depth for task hierarchies to 5 levels.
46
+ """
32
47
  self.tasks: List[ScopeMateTask] = []
33
48
  self.task_depths: Dict[str, int] = {}
34
49
  self.max_depth: int = 5 # Maximum depth of task hierarchy
@@ -37,8 +52,21 @@ class TaskEngine:
37
52
  """
38
53
  Load tasks from checkpoint file if it exists.
39
54
 
55
+ Checks for the existence of a checkpoint file and prompts the user about whether
56
+ to resume from this checkpoint. If the user confirms, loads the tasks from the
57
+ checkpoint file into the engine.
58
+
40
59
  Returns:
41
- True if checkpoint was loaded, False otherwise
60
+ bool: True if checkpoint was loaded successfully, False otherwise
61
+
62
+ Example:
63
+ ```python
64
+ engine = TaskEngine()
65
+ if engine.load_from_checkpoint():
66
+ print(f"Loaded {len(engine.tasks)} tasks from checkpoint")
67
+ else:
68
+ print("Starting with a new task list")
69
+ ```
42
70
  """
43
71
  if checkpoint_exists():
44
72
  resume = prompt_user(
@@ -58,11 +86,24 @@ class TaskEngine:
58
86
  """
59
87
  Load tasks from a user-specified file.
60
88
 
89
+ Prompts the user about whether to load an existing plan. If confirmed,
90
+ asks for the filename and attempts to load tasks from that file.
91
+
61
92
  Args:
62
- default_filename: Default filename to suggest
93
+ default_filename (str): Default filename to suggest to the user
63
94
 
64
95
  Returns:
65
- True if file was loaded, False otherwise
96
+ bool: True if file was loaded successfully, False otherwise
97
+
98
+ Raises:
99
+ FileNotFoundError: Handled internally, prints error message if file not found
100
+
101
+ Example:
102
+ ```python
103
+ engine = TaskEngine()
104
+ if engine.load_from_file("my_project_plan.json"):
105
+ print(f"Loaded {len(engine.tasks)} tasks from file")
106
+ ```
66
107
  """
67
108
  choice = prompt_user("Load existing plan?", default="n", choices=["y","n"])
68
109
  if choice.lower() == "y":
@@ -76,12 +117,58 @@ class TaskEngine:
76
117
  return False
77
118
 
78
119
  def create_new_task(self) -> None:
79
- """Create a new root task interactively."""
120
+ """
121
+ Create a new root task interactively.
122
+
123
+ Initiates an interactive dialog to build a new root task, adds it to the task list,
124
+ and automatically saves a checkpoint. The dialog collects all necessary information
125
+ for a well-defined task including title, purpose, scope, and expected outcomes.
126
+
127
+ Side Effects:
128
+ - Appends new task to self.tasks
129
+ - Saves checkpoint to disk
130
+
131
+ Example:
132
+ ```python
133
+ engine = TaskEngine()
134
+ engine.create_new_task() # Interactively creates a new root task
135
+ ```
136
+ """
80
137
  self.tasks.append(build_root_task())
81
138
  save_checkpoint(self.tasks)
82
139
 
83
140
  def breakdown_complex_tasks(self) -> None:
84
- """Process all tasks and break down complex ones."""
141
+ """
142
+ Process all tasks and break down complex ones.
143
+
144
+ This is a core function that analyzes all tasks to identify those that are too complex
145
+ or have long durations, then interactively breaks them down into smaller subtasks.
146
+ It maintains the task hierarchy, updates parent-child relationships, and ensures
147
+ estimate consistency across the task tree.
148
+
149
+ The function uses a breadth-first approach to process tasks, breaking down parent tasks
150
+ before their children, and saving checkpoints after each breakdown.
151
+
152
+ Algorithm:
153
+ 1. Initialize task depths to track hierarchy
154
+ 2. Process each task and check if it needs breakdown
155
+ 3. For tasks needing breakdown, use LLM to suggest subtasks
156
+ 4. Add approved subtasks to the task list
157
+ 5. Update parent estimates based on subtask characteristics
158
+ 6. Save checkpoint after each task breakdown
159
+
160
+ Side Effects:
161
+ - Modifies self.tasks by adding subtasks
162
+ - Updates self.task_depths with new depth information
163
+ - Saves checkpoints to disk after each breakdown
164
+
165
+ Example:
166
+ ```python
167
+ engine = TaskEngine()
168
+ engine.load_from_file("my_project.json")
169
+ engine.breakdown_complex_tasks() # Interactively breaks down complex tasks
170
+ ```
171
+ """
85
172
  # Initialize depth tracking
86
173
  self.task_depths = _initialize_task_depths(self.tasks)
87
174
 
@@ -127,7 +214,28 @@ class TaskEngine:
127
214
  save_checkpoint(self.tasks)
128
215
 
129
216
  def handle_long_duration_tasks(self) -> None:
130
- """Find and handle long duration leaf tasks."""
217
+ """
218
+ Find and handle long duration leaf tasks.
219
+
220
+ Identifies leaf tasks (tasks with no children) that have long duration estimates,
221
+ presents them to the user, and offers the opportunity to break them down further.
222
+ This helps ensure that all tasks in the final plan are of manageable size.
223
+
224
+ The function uses task_analysis.find_long_duration_leaf_tasks to identify candidates
225
+ for further breakdown, then interactively processes user-selected tasks.
226
+
227
+ Side Effects:
228
+ - May modify self.tasks by adding subtasks for long-duration leaf tasks
229
+ - Updates parent estimates via check_and_update_parent_estimates
230
+ - Saves checkpoints to disk after each breakdown
231
+
232
+ Example:
233
+ ```python
234
+ engine = TaskEngine()
235
+ engine.load_from_file("my_project.json")
236
+ engine.handle_long_duration_tasks() # Interactively processes long-duration tasks
237
+ ```
238
+ """
131
239
  # Find long duration leaf tasks
132
240
  long_duration_leaf_tasks = find_long_duration_leaf_tasks(self.tasks)
133
241
 
@@ -172,7 +280,27 @@ class TaskEngine:
172
280
  print("Invalid selection, skipping breakdown.")
173
281
 
174
282
  def finalize_plan(self) -> None:
175
- """Review and save the final plan."""
283
+ """
284
+ Review and save the final plan.
285
+
286
+ Performs a final consistency check on all task estimates, displays a summary
287
+ of the entire task hierarchy to the user, and prompts for saving the finalized
288
+ plan to a permanent file. If saved, removes the temporary checkpoint file.
289
+
290
+ Side Effects:
291
+ - Ensures consistency in parent-child estimate relationships
292
+ - Saves final plan to user-specified file
293
+ - May delete checkpoint file if plan is saved
294
+
295
+ Example:
296
+ ```python
297
+ engine = TaskEngine()
298
+ engine.load_from_file("my_project.json")
299
+ engine.breakdown_complex_tasks()
300
+ engine.handle_long_duration_tasks()
301
+ engine.finalize_plan() # Review and save final plan
302
+ ```
303
+ """
176
304
  # Final check of parent-child estimate consistency
177
305
  self.tasks = check_and_update_parent_estimates(self.tasks)
178
306
 
@@ -187,7 +315,30 @@ class TaskEngine:
187
315
  print(f"Plan left in checkpoint '{CHECKPOINT_FILE}'. Run again to resume.")
188
316
 
189
317
  def run(self) -> None:
190
- """Run the full interactive workflow."""
318
+ """
319
+ Run the full interactive workflow.
320
+
321
+ Executes the complete scopemate workflow from start to finish:
322
+ 1. Displays introduction to the user
323
+ 2. Loads existing checkpoint or creates new task
324
+ 3. Processes and breaks down complex tasks
325
+ 4. Handles long-duration leaf tasks
326
+ 5. Finalizes and saves the plan
327
+
328
+ This is the main entry point for using the TaskEngine to build a complete
329
+ task breakdown plan interactively.
330
+
331
+ Side Effects:
332
+ - Interacts with the user via console
333
+ - Modifies task list based on user input
334
+ - Creates/updates files on disk for checkpoints and final plan
335
+
336
+ Example:
337
+ ```python
338
+ engine = TaskEngine()
339
+ engine.run() # Runs the complete interactive workflow
340
+ ```
341
+ """
191
342
  # Display introduction
192
343
  print("=== scopemate Action Plan Builder ===")
193
344
  print("This tool helps break down complex tasks and maintain consistent time estimates.")
@@ -210,13 +361,40 @@ class TaskEngine:
210
361
  self.finalize_plan()
211
362
 
212
363
  def run_interactive(self) -> None:
213
- """Run the interactive mode of the application."""
364
+ """
365
+ Run the interactive mode of the application.
366
+
367
+ This is a placeholder method for running an alternative interactive mode.
368
+ Currently just prints the header and would be extended in future versions.
369
+
370
+ Example:
371
+ ```python
372
+ engine = TaskEngine()
373
+ engine.run_interactive() # Would run an alternative interactive mode
374
+ ```
375
+ """
214
376
  print("=== scopemate Action Plan Builder ===")
215
377
 
216
378
 
217
379
  def interactive_builder():
218
380
  """
219
381
  Legacy function for backward compatibility that runs the TaskEngine.
382
+
383
+ Creates a TaskEngine instance and runs the full workflow, handling
384
+ KeyboardInterrupt exceptions by saving progress to a checkpoint.
385
+
386
+ This function provides backward compatibility with older versions
387
+ that used this entry point directly.
388
+
389
+ Side Effects:
390
+ - Creates and runs a TaskEngine instance
391
+ - Handles KeyboardInterrupt by saving checkpoint
392
+
393
+ Example:
394
+ ```python
395
+ from scopemate.engine import interactive_builder
396
+ interactive_builder() # Runs the complete workflow
397
+ ```
220
398
  """
221
399
  engine = TaskEngine()
222
400
  try:
scopemate/interaction.py CHANGED
@@ -25,13 +25,43 @@ def prompt_user(
25
25
  """
26
26
  Prompt user for input with optional default and choices validation.
27
27
 
28
+ This function is the primary method for all user interaction in scopemate. It
29
+ handles displaying prompts, validating input against constraints, and providing
30
+ default values when the user presses Enter without typing anything.
31
+
32
+ The function implements a validation loop that ensures users can only provide
33
+ valid input. If choices are specified, the function performs case-insensitive
34
+ validation against those choices and re-prompts when input is invalid.
35
+
36
+ Features:
37
+ - Displays a prompt with an optional default value in square brackets
38
+ - Supports empty input with a default fallback value
39
+ - Validates input against a predefined set of case-insensitive choices
40
+ - Provides clear error messages when input doesn't match required choices
41
+ - Loops until valid input is received
42
+
28
43
  Args:
29
- prompt: The prompt text to display
30
- default: Optional default value if user enters nothing
31
- choices: Optional list of valid choices
44
+ prompt (str): The prompt text to display to the user
45
+ default (Optional[str]): Default value to use if user submits empty input
46
+ choices (Optional[List[str]]): List of valid input choices for validation
32
47
 
33
48
  Returns:
34
- User's validated input as a string
49
+ str: The validated user input
50
+
51
+ Example:
52
+ ```python
53
+ # Simple prompt with no constraints
54
+ name = prompt_user("Enter your name")
55
+
56
+ # Prompt with a default value
57
+ team = prompt_user("Select team", default="Backend")
58
+
59
+ # Prompt with choices validation
60
+ response = prompt_user("Continue?", default="y", choices=["y", "n"])
61
+ if response.lower() == "y":
62
+ # Proceed with action
63
+ pass
64
+ ```
35
65
  """
36
66
  while True:
37
67
  suffix = f" [{default}]" if default is not None else ""