galangal-orchestrate 0.13.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.
- galangal/__init__.py +36 -0
- galangal/__main__.py +6 -0
- galangal/ai/__init__.py +167 -0
- galangal/ai/base.py +159 -0
- galangal/ai/claude.py +352 -0
- galangal/ai/codex.py +370 -0
- galangal/ai/gemini.py +43 -0
- galangal/ai/subprocess.py +254 -0
- galangal/cli.py +371 -0
- galangal/commands/__init__.py +27 -0
- galangal/commands/complete.py +367 -0
- galangal/commands/github.py +355 -0
- galangal/commands/init.py +177 -0
- galangal/commands/init_wizard.py +762 -0
- galangal/commands/list.py +20 -0
- galangal/commands/pause.py +34 -0
- galangal/commands/prompts.py +89 -0
- galangal/commands/reset.py +41 -0
- galangal/commands/resume.py +30 -0
- galangal/commands/skip.py +62 -0
- galangal/commands/start.py +530 -0
- galangal/commands/status.py +44 -0
- galangal/commands/switch.py +28 -0
- galangal/config/__init__.py +15 -0
- galangal/config/defaults.py +183 -0
- galangal/config/loader.py +163 -0
- galangal/config/schema.py +330 -0
- galangal/core/__init__.py +33 -0
- galangal/core/artifacts.py +136 -0
- galangal/core/state.py +1097 -0
- galangal/core/tasks.py +454 -0
- galangal/core/utils.py +116 -0
- galangal/core/workflow/__init__.py +68 -0
- galangal/core/workflow/core.py +789 -0
- galangal/core/workflow/engine.py +781 -0
- galangal/core/workflow/pause.py +35 -0
- galangal/core/workflow/tui_runner.py +1322 -0
- galangal/exceptions.py +36 -0
- galangal/github/__init__.py +31 -0
- galangal/github/client.py +427 -0
- galangal/github/images.py +324 -0
- galangal/github/issues.py +298 -0
- galangal/logging.py +364 -0
- galangal/prompts/__init__.py +5 -0
- galangal/prompts/builder.py +527 -0
- galangal/prompts/defaults/benchmark.md +34 -0
- galangal/prompts/defaults/contract.md +35 -0
- galangal/prompts/defaults/design.md +54 -0
- galangal/prompts/defaults/dev.md +89 -0
- galangal/prompts/defaults/docs.md +104 -0
- galangal/prompts/defaults/migration.md +59 -0
- galangal/prompts/defaults/pm.md +110 -0
- galangal/prompts/defaults/pm_questions.md +53 -0
- galangal/prompts/defaults/preflight.md +32 -0
- galangal/prompts/defaults/qa.md +65 -0
- galangal/prompts/defaults/review.md +90 -0
- galangal/prompts/defaults/review_codex.md +99 -0
- galangal/prompts/defaults/security.md +84 -0
- galangal/prompts/defaults/test.md +91 -0
- galangal/results.py +176 -0
- galangal/ui/__init__.py +5 -0
- galangal/ui/console.py +126 -0
- galangal/ui/tui/__init__.py +56 -0
- galangal/ui/tui/adapters.py +168 -0
- galangal/ui/tui/app.py +902 -0
- galangal/ui/tui/entry.py +24 -0
- galangal/ui/tui/mixins.py +196 -0
- galangal/ui/tui/modals.py +339 -0
- galangal/ui/tui/styles/app.tcss +86 -0
- galangal/ui/tui/styles/modals.tcss +197 -0
- galangal/ui/tui/types.py +107 -0
- galangal/ui/tui/widgets.py +263 -0
- galangal/validation/__init__.py +5 -0
- galangal/validation/runner.py +1072 -0
- galangal_orchestrate-0.13.0.dist-info/METADATA +985 -0
- galangal_orchestrate-0.13.0.dist-info/RECORD +79 -0
- galangal_orchestrate-0.13.0.dist-info/WHEEL +4 -0
- galangal_orchestrate-0.13.0.dist-info/entry_points.txt +2 -0
- galangal_orchestrate-0.13.0.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
galangal list - List all tasks.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from galangal.core.tasks import get_active_task, list_tasks
|
|
8
|
+
from galangal.ui.console import console, display_task_list
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def cmd_list(args: argparse.Namespace) -> int:
|
|
12
|
+
"""List all tasks."""
|
|
13
|
+
tasks = list_tasks()
|
|
14
|
+
active = get_active_task()
|
|
15
|
+
|
|
16
|
+
display_task_list(tasks, active)
|
|
17
|
+
|
|
18
|
+
if active:
|
|
19
|
+
console.print("\n[dim]→ = active task[/dim]")
|
|
20
|
+
return 0
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
galangal pause - Pause the active task.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from galangal.core.state import Stage
|
|
8
|
+
from galangal.core.tasks import ensure_active_task_with_state
|
|
9
|
+
from galangal.ui.console import console, print_info
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def cmd_pause(args: argparse.Namespace) -> int:
|
|
13
|
+
"""Pause the active task for a break or shutdown."""
|
|
14
|
+
active, state = ensure_active_task_with_state()
|
|
15
|
+
if not active or not state:
|
|
16
|
+
return 1
|
|
17
|
+
|
|
18
|
+
if state.stage == Stage.COMPLETE:
|
|
19
|
+
print_info(f"Task '{active}' is already complete.")
|
|
20
|
+
console.print("Use 'complete' to create PR and move to done/.")
|
|
21
|
+
return 0
|
|
22
|
+
|
|
23
|
+
console.print("\n" + "=" * 60)
|
|
24
|
+
console.print("[yellow]⏸️ TASK PAUSED[/yellow]")
|
|
25
|
+
console.print("=" * 60)
|
|
26
|
+
console.print(f"\nTask: {state.task_name}")
|
|
27
|
+
console.print(f"Stage: {state.stage.value} (attempt {state.attempt})")
|
|
28
|
+
console.print(f"Type: {state.task_type.display_name()}")
|
|
29
|
+
console.print(f"Description: {state.task_description[:60]}...")
|
|
30
|
+
console.print("\nYour progress is saved. You can safely shut down now.")
|
|
31
|
+
console.print("\nTo resume later, run:")
|
|
32
|
+
console.print(" [cyan]galangal resume[/cyan]")
|
|
33
|
+
console.print("=" * 60)
|
|
34
|
+
return 0
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""
|
|
2
|
+
galangal prompts - Manage stage prompts.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import shutil
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from galangal.config.loader import get_prompts_dir
|
|
10
|
+
from galangal.core.state import parse_stage_arg
|
|
11
|
+
from galangal.prompts.builder import PromptBuilder
|
|
12
|
+
from galangal.ui.console import console, print_error, print_info, print_success
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def cmd_prompts_export(args: argparse.Namespace) -> int:
|
|
16
|
+
"""Export default prompts to .galangal/prompts/ for customization."""
|
|
17
|
+
prompts_dir = get_prompts_dir()
|
|
18
|
+
defaults_dir = Path(__file__).parent.parent / "prompts" / "defaults"
|
|
19
|
+
|
|
20
|
+
if not defaults_dir.exists():
|
|
21
|
+
print_error("Default prompts directory not found.")
|
|
22
|
+
return 1
|
|
23
|
+
|
|
24
|
+
prompts_dir.mkdir(parents=True, exist_ok=True)
|
|
25
|
+
|
|
26
|
+
exported = []
|
|
27
|
+
skipped = []
|
|
28
|
+
|
|
29
|
+
for prompt_file in defaults_dir.glob("*.md"):
|
|
30
|
+
dest = prompts_dir / prompt_file.name
|
|
31
|
+
if dest.exists():
|
|
32
|
+
skipped.append(prompt_file.name)
|
|
33
|
+
else:
|
|
34
|
+
shutil.copy(prompt_file, dest)
|
|
35
|
+
exported.append(prompt_file.name)
|
|
36
|
+
|
|
37
|
+
if exported:
|
|
38
|
+
print_success(f"Exported {len(exported)} prompts to .galangal/prompts/")
|
|
39
|
+
for name in exported:
|
|
40
|
+
console.print(f" [green]✓[/green] {name}")
|
|
41
|
+
|
|
42
|
+
if skipped:
|
|
43
|
+
print_info(f"Skipped {len(skipped)} existing prompts (won't overwrite)")
|
|
44
|
+
for name in skipped:
|
|
45
|
+
console.print(f" [dim]○[/dim] {name}")
|
|
46
|
+
|
|
47
|
+
console.print("\n[dim]Edit these files to customize prompts for your project.[/dim]")
|
|
48
|
+
console.print("[dim]Project prompts override package defaults.[/dim]")
|
|
49
|
+
|
|
50
|
+
return 0
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def cmd_prompts_show(args: argparse.Namespace) -> int:
|
|
54
|
+
"""Show the effective prompt for a stage."""
|
|
55
|
+
# Parse stage (COMPLETE excluded - it has no prompt)
|
|
56
|
+
stage = parse_stage_arg(args.stage, exclude_complete=True)
|
|
57
|
+
if stage is None:
|
|
58
|
+
return 1
|
|
59
|
+
|
|
60
|
+
builder = PromptBuilder()
|
|
61
|
+
prompt = builder.get_stage_prompt(stage)
|
|
62
|
+
|
|
63
|
+
# Determine source
|
|
64
|
+
prompts_dir = get_prompts_dir()
|
|
65
|
+
override_path = prompts_dir / f"{stage.value.lower()}.md"
|
|
66
|
+
source = "project override" if override_path.exists() else "package default"
|
|
67
|
+
|
|
68
|
+
console.print(f"\n[bold]Stage:[/bold] {stage.value}")
|
|
69
|
+
console.print(f"[bold]Source:[/bold] {source}")
|
|
70
|
+
console.print("[dim]" + "=" * 60 + "[/dim]")
|
|
71
|
+
console.print(prompt)
|
|
72
|
+
console.print("[dim]" + "=" * 60 + "[/dim]")
|
|
73
|
+
|
|
74
|
+
return 0
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def cmd_prompts(args: argparse.Namespace) -> int:
|
|
78
|
+
"""Prompts management command router."""
|
|
79
|
+
if hasattr(args, "prompts_command") and args.prompts_command:
|
|
80
|
+
if args.prompts_command == "export":
|
|
81
|
+
return cmd_prompts_export(args)
|
|
82
|
+
elif args.prompts_command == "show":
|
|
83
|
+
return cmd_prompts_show(args)
|
|
84
|
+
|
|
85
|
+
console.print("Usage: galangal prompts <command>")
|
|
86
|
+
console.print("\nCommands:")
|
|
87
|
+
console.print(" export Export default prompts for customization")
|
|
88
|
+
console.print(" show Show effective prompt for a stage")
|
|
89
|
+
return 1
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
galangal reset - Delete the active task.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import shutil
|
|
7
|
+
|
|
8
|
+
from rich.prompt import Prompt
|
|
9
|
+
|
|
10
|
+
from galangal.core.state import get_task_dir
|
|
11
|
+
from galangal.core.tasks import clear_active_task, get_active_task
|
|
12
|
+
from galangal.ui.console import print_error, print_info, print_success
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def cmd_reset(args: argparse.Namespace) -> int:
|
|
16
|
+
"""Delete the active task."""
|
|
17
|
+
active = get_active_task()
|
|
18
|
+
if not active:
|
|
19
|
+
print_error("No active task.")
|
|
20
|
+
return 0
|
|
21
|
+
|
|
22
|
+
task_dir = get_task_dir(active)
|
|
23
|
+
if not task_dir.exists():
|
|
24
|
+
print_info("Task directory not found.")
|
|
25
|
+
clear_active_task()
|
|
26
|
+
return 0
|
|
27
|
+
|
|
28
|
+
if not args.force:
|
|
29
|
+
confirm = (
|
|
30
|
+
Prompt.ask(f"Delete task '{active}' and all its artifacts? [y/N]", default="n")
|
|
31
|
+
.strip()
|
|
32
|
+
.lower()
|
|
33
|
+
)
|
|
34
|
+
if confirm != "y":
|
|
35
|
+
print_info("Reset cancelled.")
|
|
36
|
+
return 1
|
|
37
|
+
|
|
38
|
+
shutil.rmtree(task_dir)
|
|
39
|
+
clear_active_task()
|
|
40
|
+
print_success(f"Task '{active}' deleted.")
|
|
41
|
+
return 0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
galangal resume - Resume the active task.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from galangal.core.tasks import ensure_active_task_with_state
|
|
8
|
+
from galangal.core.workflow import run_workflow
|
|
9
|
+
from galangal.ui.console import console
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def cmd_resume(args: argparse.Namespace) -> int:
|
|
13
|
+
"""Resume the active task."""
|
|
14
|
+
active, state = ensure_active_task_with_state(
|
|
15
|
+
no_task_msg="No active task. Use 'list' to see tasks, 'switch' to select one."
|
|
16
|
+
)
|
|
17
|
+
if not active or not state:
|
|
18
|
+
return 1
|
|
19
|
+
|
|
20
|
+
console.print(f"[bold]Resuming task:[/bold] {active}")
|
|
21
|
+
console.print(f"[dim]Stage:[/dim] {state.stage.value}")
|
|
22
|
+
console.print(f"[dim]Type:[/dim] {state.task_type.display_name()}")
|
|
23
|
+
|
|
24
|
+
# Pass skip_discovery flag via state attribute
|
|
25
|
+
if getattr(args, "skip_discovery", False):
|
|
26
|
+
state._skip_discovery = True
|
|
27
|
+
console.print("[dim]Discovery Q&A:[/dim] skipped")
|
|
28
|
+
|
|
29
|
+
run_workflow(state)
|
|
30
|
+
return 0
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
galangal skip-to - Jump to a specific stage for debugging.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from rich.prompt import Prompt
|
|
8
|
+
|
|
9
|
+
from galangal.core.state import STAGE_ORDER, parse_stage_arg, save_state
|
|
10
|
+
from galangal.core.tasks import ensure_active_task_with_state
|
|
11
|
+
from galangal.core.workflow import run_workflow
|
|
12
|
+
from galangal.ui.console import console, print_info, print_success
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def cmd_skip_to(args: argparse.Namespace) -> int:
|
|
16
|
+
"""Jump to a specific stage (for debugging/re-running)."""
|
|
17
|
+
active, state = ensure_active_task_with_state()
|
|
18
|
+
if not active or not state:
|
|
19
|
+
return 1
|
|
20
|
+
|
|
21
|
+
# Parse target stage (COMPLETE not allowed - use 'complete' command instead)
|
|
22
|
+
target_stage = parse_stage_arg(args.stage, exclude_complete=True)
|
|
23
|
+
if target_stage is None:
|
|
24
|
+
return 1
|
|
25
|
+
|
|
26
|
+
current_stage = state.stage
|
|
27
|
+
current_idx = STAGE_ORDER.index(current_stage) if current_stage in STAGE_ORDER else -1
|
|
28
|
+
target_idx = STAGE_ORDER.index(target_stage)
|
|
29
|
+
|
|
30
|
+
# Warn if skipping backwards or forwards
|
|
31
|
+
if target_idx < current_idx:
|
|
32
|
+
console.print(
|
|
33
|
+
f"[yellow]⚠️ Going backwards: {current_stage.value} → {target_stage.value}[/yellow]"
|
|
34
|
+
)
|
|
35
|
+
elif target_idx > current_idx:
|
|
36
|
+
console.print(
|
|
37
|
+
f"[yellow]⚠️ Skipping forward: {current_stage.value} → {target_stage.value}[/yellow]"
|
|
38
|
+
)
|
|
39
|
+
else:
|
|
40
|
+
console.print(f"[dim]Re-running current stage: {target_stage.value}[/dim]")
|
|
41
|
+
|
|
42
|
+
if not args.force:
|
|
43
|
+
confirm = Prompt.ask(f"Jump to {target_stage.value}? [y/N]", default="n").strip().lower()
|
|
44
|
+
if confirm != "y":
|
|
45
|
+
print_info("Cancelled.")
|
|
46
|
+
return 0
|
|
47
|
+
|
|
48
|
+
# Update state
|
|
49
|
+
state.stage = target_stage
|
|
50
|
+
state.reset_attempts()
|
|
51
|
+
state.awaiting_approval = False
|
|
52
|
+
state.clarification_required = False
|
|
53
|
+
save_state(state)
|
|
54
|
+
|
|
55
|
+
print_success(f"Jumped to stage: {target_stage.value}")
|
|
56
|
+
|
|
57
|
+
# Optionally resume immediately
|
|
58
|
+
if args.resume:
|
|
59
|
+
console.print("\n[dim]Resuming workflow...[/dim]")
|
|
60
|
+
run_workflow(state)
|
|
61
|
+
|
|
62
|
+
return 0
|