loki-mode 6.0.0 → 6.1.0
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.
- package/README.md +20 -0
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/bmad-adapter.py +776 -0
- package/autonomy/loki +78 -0
- package/autonomy/prd-analyzer.py +26 -4
- package/autonomy/run.sh +149 -4
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/architecture/bmad-integration-epic.md +271 -0
- package/docs/architecture/bmad-integration-review.md +86 -0
- package/docs/architecture/bmad-integration-validation.md +249 -0
- package/docs/architecture/bmad-loki-voice-agent-council-analysis.md +61 -0
- package/mcp/__init__.py +1 -1
- package/mcp/requirements.txt +1 -0
- package/mcp/server.py +152 -0
- package/package.json +1 -1
package/autonomy/loki
CHANGED
|
@@ -438,6 +438,7 @@ show_help() {
|
|
|
438
438
|
echo " --skip-memory Skip loading memory context at startup"
|
|
439
439
|
echo " --compliance PRESET Enable compliance mode (default|healthcare|fintech|government)"
|
|
440
440
|
echo " --budget USD Set cost budget limit (display in dashboard/status)"
|
|
441
|
+
echo " --bmad-project PATH Use BMAD Method project artifacts as input"
|
|
441
442
|
echo ""
|
|
442
443
|
echo "Options for 'run' (v6.0.0):"
|
|
443
444
|
echo " --dry-run Preview generated PRD without starting"
|
|
@@ -473,6 +474,7 @@ cmd_start() {
|
|
|
473
474
|
local args=()
|
|
474
475
|
local prd_file=""
|
|
475
476
|
local provider=""
|
|
477
|
+
local bmad_project_path=""
|
|
476
478
|
|
|
477
479
|
while [[ $# -gt 0 ]]; do
|
|
478
480
|
case "$1" in
|
|
@@ -496,6 +498,7 @@ cmd_start() {
|
|
|
496
498
|
echo " --skip-memory Skip loading memory context at startup"
|
|
497
499
|
echo " --compliance PRESET Enable compliance mode (default|healthcare|fintech|government)"
|
|
498
500
|
echo " --budget USD Cost budget limit (auto-pause when exceeded)"
|
|
501
|
+
echo " --bmad-project PATH Use BMAD Method project artifacts as input"
|
|
499
502
|
echo " --yes, -y Skip confirmation prompts (auto-confirm)"
|
|
500
503
|
echo ""
|
|
501
504
|
echo "Environment Variables:"
|
|
@@ -510,6 +513,7 @@ cmd_start() {
|
|
|
510
513
|
echo " loki start ./prd.md # Start with PRD file"
|
|
511
514
|
echo " loki start ./prd.md --parallel # Parallel mode with worktrees"
|
|
512
515
|
echo " loki start --provider codex # Use OpenAI Codex CLI"
|
|
516
|
+
echo " loki start --bmad-project ./my-project # Start from BMAD artifacts"
|
|
513
517
|
echo " loki start --yes # Skip confirmation prompt"
|
|
514
518
|
echo " LOKI_PRD_FILE=./prd.md loki start # PRD via env var"
|
|
515
519
|
exit 0
|
|
@@ -581,6 +585,19 @@ cmd_start() {
|
|
|
581
585
|
export LOKI_AUTO_CONFIRM=true
|
|
582
586
|
shift
|
|
583
587
|
;;
|
|
588
|
+
--bmad-project)
|
|
589
|
+
if [[ -n "${2:-}" ]]; then
|
|
590
|
+
bmad_project_path="$2"
|
|
591
|
+
shift 2
|
|
592
|
+
else
|
|
593
|
+
echo -e "${RED}--bmad-project requires a path to a BMAD project directory${NC}"
|
|
594
|
+
exit 1
|
|
595
|
+
fi
|
|
596
|
+
;;
|
|
597
|
+
--bmad-project=*)
|
|
598
|
+
bmad_project_path="${1#*=}"
|
|
599
|
+
shift
|
|
600
|
+
;;
|
|
584
601
|
--budget)
|
|
585
602
|
if [[ -n "${2:-}" ]]; then
|
|
586
603
|
if ! echo "$2" | grep -qE '^[0-9]+(\.[0-9]+)?$'; then
|
|
@@ -619,6 +636,67 @@ cmd_start() {
|
|
|
619
636
|
prd_file="$LOKI_PRD_FILE"
|
|
620
637
|
fi
|
|
621
638
|
|
|
639
|
+
# BMAD project validation and adapter execution
|
|
640
|
+
if [[ -n "$bmad_project_path" ]]; then
|
|
641
|
+
# Resolve to absolute path
|
|
642
|
+
if [[ ! "$bmad_project_path" = /* ]]; then
|
|
643
|
+
bmad_project_path="$(cd "$bmad_project_path" 2>/dev/null && pwd)" || {
|
|
644
|
+
echo -e "${RED}Error: BMAD project path does not exist: $bmad_project_path${NC}"
|
|
645
|
+
exit 1
|
|
646
|
+
}
|
|
647
|
+
fi
|
|
648
|
+
|
|
649
|
+
# Validate path is a directory
|
|
650
|
+
if [[ ! -d "$bmad_project_path" ]]; then
|
|
651
|
+
echo -e "${RED}Error: BMAD project path is not a directory: $bmad_project_path${NC}"
|
|
652
|
+
exit 1
|
|
653
|
+
fi
|
|
654
|
+
|
|
655
|
+
# Check for BMAD artifacts (_bmad-output/ or common BMAD markers)
|
|
656
|
+
if [[ ! -d "$bmad_project_path/_bmad-output" ]]; then
|
|
657
|
+
echo -e "${YELLOW}Warning: No _bmad-output/ directory found in $bmad_project_path${NC}"
|
|
658
|
+
echo "Expected a BMAD Method project with _bmad-output/ artifacts."
|
|
659
|
+
echo -e "Continue anyway? [y/N] \\c"
|
|
660
|
+
local _auto="${LOKI_AUTO_CONFIRM:-${CI:-false}}"
|
|
661
|
+
if [[ "$_auto" == "true" ]]; then
|
|
662
|
+
echo "y (auto-confirmed)"
|
|
663
|
+
else
|
|
664
|
+
read -r _bmad_confirm
|
|
665
|
+
if [[ ! "$_bmad_confirm" =~ ^[Yy] ]]; then
|
|
666
|
+
echo "Aborted."
|
|
667
|
+
exit 0
|
|
668
|
+
fi
|
|
669
|
+
fi
|
|
670
|
+
fi
|
|
671
|
+
|
|
672
|
+
# Export for run.sh to access
|
|
673
|
+
export BMAD_PROJECT_PATH="$bmad_project_path"
|
|
674
|
+
|
|
675
|
+
# Ensure .loki directory exists for adapter output
|
|
676
|
+
mkdir -p "$LOKI_DIR"
|
|
677
|
+
|
|
678
|
+
# Run the BMAD adapter to normalize artifacts
|
|
679
|
+
echo -e "${CYAN}Running BMAD adapter...${NC}"
|
|
680
|
+
local adapter_script="${SCRIPT_DIR:-$(dirname "$0")}/bmad-adapter.py"
|
|
681
|
+
if [[ ! -f "$adapter_script" ]]; then
|
|
682
|
+
echo -e "${RED}Error: BMAD adapter not found at $adapter_script${NC}"
|
|
683
|
+
echo "Please ensure autonomy/bmad-adapter.py exists."
|
|
684
|
+
exit 1
|
|
685
|
+
fi
|
|
686
|
+
|
|
687
|
+
if ! python3 "$adapter_script" "$bmad_project_path" --output-dir "$LOKI_DIR" --validate; then
|
|
688
|
+
echo -e "${RED}Error: BMAD adapter failed. Check the project artifacts.${NC}"
|
|
689
|
+
exit 1
|
|
690
|
+
fi
|
|
691
|
+
echo -e "${GREEN}BMAD artifacts normalized successfully.${NC}"
|
|
692
|
+
|
|
693
|
+
# If no explicit PRD was provided, use the normalized BMAD PRD
|
|
694
|
+
if [[ -z "$prd_file" ]] && [[ -f "$LOKI_DIR/bmad-prd-normalized.md" ]]; then
|
|
695
|
+
prd_file="$LOKI_DIR/bmad-prd-normalized.md"
|
|
696
|
+
echo -e "${CYAN}Using normalized BMAD PRD: $prd_file${NC}"
|
|
697
|
+
fi
|
|
698
|
+
fi
|
|
699
|
+
|
|
622
700
|
if [ -n "$prd_file" ]; then
|
|
623
701
|
args+=("$prd_file")
|
|
624
702
|
else
|
package/autonomy/prd-analyzer.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""PRD Quality Analyzer for Loki Mode
|
|
2
|
+
"""PRD Quality Analyzer for Loki Mode v6.1.0
|
|
3
3
|
|
|
4
4
|
Analyzes PRD structure and completeness using regex-based heuristics.
|
|
5
5
|
Writes observations to .loki/prd-observations.md with quality score,
|
|
@@ -10,6 +10,7 @@ Stdlib only - no pip dependencies required.
|
|
|
10
10
|
Usage:
|
|
11
11
|
python3 prd-analyzer.py path/to/prd.md --output .loki/prd-observations.md
|
|
12
12
|
python3 prd-analyzer.py path/to/prd.md --output .loki/prd-observations.md --interactive
|
|
13
|
+
python3 prd-analyzer.py path/to/prd.md --architecture path/to/architecture.md
|
|
13
14
|
"""
|
|
14
15
|
|
|
15
16
|
import argparse
|
|
@@ -28,10 +29,13 @@ DIMENSIONS = {
|
|
|
28
29
|
"weight": 1.5,
|
|
29
30
|
"heading_patterns": [
|
|
30
31
|
r"(?i)#+\s.*(?:feature|requirement|scope|functional|capability)",
|
|
32
|
+
r"(?i)^##\s+Functional\s+Requirements",
|
|
33
|
+
r"(?i)^##\s+Product\s+Scope",
|
|
31
34
|
],
|
|
32
35
|
"content_patterns": [
|
|
33
36
|
r"^\s*[-*]\s+\S",
|
|
34
37
|
r"^\s*\d+\.\s+\S",
|
|
38
|
+
r"(?i)^FR\d+:",
|
|
35
39
|
],
|
|
36
40
|
"description": "Numbered or bulleted list of features/requirements",
|
|
37
41
|
},
|
|
@@ -55,6 +59,7 @@ DIMENSIONS = {
|
|
|
55
59
|
"weight": 1.0,
|
|
56
60
|
"heading_patterns": [
|
|
57
61
|
r"(?i)#+\s.*(?:user\s+(?:stor|flow|journey)|persona|use\s+case)",
|
|
62
|
+
r"(?i)^##\s+User\s+Journeys",
|
|
58
63
|
],
|
|
59
64
|
"content_patterns": [
|
|
60
65
|
r"(?i)\bas\s+a\s+\w+",
|
|
@@ -68,11 +73,13 @@ DIMENSIONS = {
|
|
|
68
73
|
"weight": 1.0,
|
|
69
74
|
"heading_patterns": [
|
|
70
75
|
r"(?i)#+\s.*(?:acceptance|criteria|definition\s+of\s+done|done\s+when)",
|
|
76
|
+
r"(?i)^##\s+Success\s+Criteria",
|
|
71
77
|
],
|
|
72
78
|
"content_patterns": [
|
|
73
79
|
r"^\s*-\s*\[\s*[xX ]?\s*\]",
|
|
74
80
|
r"(?i)\bgiven\b.*\bwhen\b.*\bthen\b",
|
|
75
81
|
r"(?i)\bmust\s+(?:be|have|support|handle)\b",
|
|
82
|
+
r"(?i)^\s*\*\*Given\*\*",
|
|
76
83
|
],
|
|
77
84
|
"description": "Measurable completion criteria or checklists",
|
|
78
85
|
},
|
|
@@ -107,6 +114,7 @@ DIMENSIONS = {
|
|
|
107
114
|
"weight": 0.75,
|
|
108
115
|
"heading_patterns": [
|
|
109
116
|
r"(?i)#+\s.*(?:deploy|hosting|infra|ci.?cd|environment)",
|
|
117
|
+
r"(?i)^##\s+Non-Functional\s+Requirements",
|
|
110
118
|
],
|
|
111
119
|
"content_patterns": [
|
|
112
120
|
r"(?i)\b(?:deploy|hosting|ci.?cd|pipeline|staging|production)\b",
|
|
@@ -131,6 +139,7 @@ DIMENSIONS = {
|
|
|
131
139
|
"weight": 1.0,
|
|
132
140
|
"heading_patterns": [
|
|
133
141
|
r"(?i)#+\s.*(?:security|auth|permission|access\s+control)",
|
|
142
|
+
r"(?i)^##\s+Non-Functional\s+Requirements",
|
|
134
143
|
],
|
|
135
144
|
"content_patterns": [
|
|
136
145
|
r"(?i)\b(?:auth(?:entication|orization)?|oauth|jwt|token)\b",
|
|
@@ -153,8 +162,9 @@ SCOPE_THRESHOLDS = [
|
|
|
153
162
|
class PrdAnalyzer:
|
|
154
163
|
"""Analyzes PRD quality and completeness."""
|
|
155
164
|
|
|
156
|
-
def __init__(self, prd_path):
|
|
165
|
+
def __init__(self, prd_path, architecture_path=None):
|
|
157
166
|
self.prd_path = Path(prd_path)
|
|
167
|
+
self.architecture_path = Path(architecture_path) if architecture_path else None
|
|
158
168
|
self.content = ""
|
|
159
169
|
self.lines = []
|
|
160
170
|
self.results = {}
|
|
@@ -163,13 +173,20 @@ class PrdAnalyzer:
|
|
|
163
173
|
self.score = 0.0
|
|
164
174
|
|
|
165
175
|
def load(self):
|
|
166
|
-
"""Load and validate the PRD file."""
|
|
176
|
+
"""Load and validate the PRD file, optionally appending architecture doc."""
|
|
167
177
|
if not self.prd_path.exists():
|
|
168
178
|
raise FileNotFoundError(f"PRD file not found: {self.prd_path}")
|
|
169
179
|
self.content = self.prd_path.read_text(encoding="utf-8", errors="replace")
|
|
170
180
|
if not self.content.strip():
|
|
171
181
|
raise ValueError(f"PRD file is empty: {self.prd_path}")
|
|
172
182
|
self.lines = self.content.splitlines()
|
|
183
|
+
# Append architecture document lines for pattern matching
|
|
184
|
+
if self.architecture_path:
|
|
185
|
+
if not self.architecture_path.exists():
|
|
186
|
+
raise FileNotFoundError(f"Architecture file not found: {self.architecture_path}")
|
|
187
|
+
arch_content = self.architecture_path.read_text(encoding="utf-8", errors="replace")
|
|
188
|
+
if arch_content.strip():
|
|
189
|
+
self.lines.extend(arch_content.splitlines())
|
|
173
190
|
|
|
174
191
|
def analyze(self):
|
|
175
192
|
"""Run all analysis dimensions and compute score."""
|
|
@@ -420,6 +437,11 @@ def main():
|
|
|
420
437
|
default=".loki/prd-observations.md",
|
|
421
438
|
help="Output path for observations (default: .loki/prd-observations.md)",
|
|
422
439
|
)
|
|
440
|
+
parser.add_argument(
|
|
441
|
+
"--architecture",
|
|
442
|
+
metavar="PATH",
|
|
443
|
+
help="Optional architecture document to include in analysis",
|
|
444
|
+
)
|
|
423
445
|
parser.add_argument(
|
|
424
446
|
"--interactive",
|
|
425
447
|
action="store_true",
|
|
@@ -428,7 +450,7 @@ def main():
|
|
|
428
450
|
args = parser.parse_args()
|
|
429
451
|
|
|
430
452
|
try:
|
|
431
|
-
analyzer = PrdAnalyzer(args.prd_path)
|
|
453
|
+
analyzer = PrdAnalyzer(args.prd_path, architecture_path=args.architecture)
|
|
432
454
|
analyzer.analyze()
|
|
433
455
|
observations = analyzer.generate_observations()
|
|
434
456
|
|
package/autonomy/run.sh
CHANGED
|
@@ -7034,21 +7034,163 @@ except: pass
|
|
|
7034
7034
|
" 2>/dev/null || true)
|
|
7035
7035
|
fi
|
|
7036
7036
|
|
|
7037
|
+
# BMAD context injection (if available)
|
|
7038
|
+
local bmad_context=""
|
|
7039
|
+
if [[ -f ".loki/bmad-metadata.json" ]]; then
|
|
7040
|
+
local bmad_arch=""
|
|
7041
|
+
if [[ -f ".loki/bmad-architecture-summary.md" ]]; then
|
|
7042
|
+
bmad_arch=$(head -c 16000 ".loki/bmad-architecture-summary.md")
|
|
7043
|
+
fi
|
|
7044
|
+
local bmad_tasks=""
|
|
7045
|
+
if [[ -f ".loki/bmad-tasks.json" ]]; then
|
|
7046
|
+
bmad_tasks=$(head -c 32000 ".loki/bmad-tasks.json")
|
|
7047
|
+
fi
|
|
7048
|
+
local bmad_validation=""
|
|
7049
|
+
if [[ -f ".loki/bmad-validation.md" ]]; then
|
|
7050
|
+
bmad_validation=$(head -c 8000 ".loki/bmad-validation.md")
|
|
7051
|
+
fi
|
|
7052
|
+
bmad_context="BMAD_CONTEXT: This project uses BMAD Method structured artifacts. Architecture decisions and epic/story breakdown are provided below."
|
|
7053
|
+
if [[ -n "$bmad_arch" ]]; then
|
|
7054
|
+
bmad_context="$bmad_context ARCHITECTURE DECISIONS: $bmad_arch"
|
|
7055
|
+
fi
|
|
7056
|
+
if [[ -n "$bmad_tasks" ]]; then
|
|
7057
|
+
bmad_context="$bmad_context EPIC/STORY TASKS (from BMAD): $bmad_tasks"
|
|
7058
|
+
fi
|
|
7059
|
+
if [[ -n "$bmad_validation" ]]; then
|
|
7060
|
+
bmad_context="$bmad_context ARTIFACT VALIDATION: $bmad_validation"
|
|
7061
|
+
fi
|
|
7062
|
+
fi
|
|
7063
|
+
|
|
7037
7064
|
if [ $retry -eq 0 ]; then
|
|
7038
7065
|
if [ -n "$prd" ]; then
|
|
7039
|
-
echo "Loki Mode with PRD at $prd. $human_directive $queue_tasks $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $compaction_reminder $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
7066
|
+
echo "Loki Mode with PRD at $prd. $human_directive $queue_tasks $bmad_context $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $compaction_reminder $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
7040
7067
|
else
|
|
7041
|
-
echo "Loki Mode. $human_directive $queue_tasks $checklist_status $app_runner_info $playwright_info $memory_context_section $analysis_instruction $rarv_instruction $memory_instruction $compaction_reminder $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
7068
|
+
echo "Loki Mode. $human_directive $queue_tasks $bmad_context $checklist_status $app_runner_info $playwright_info $memory_context_section $analysis_instruction $rarv_instruction $memory_instruction $compaction_reminder $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
7042
7069
|
fi
|
|
7043
7070
|
else
|
|
7044
7071
|
if [ -n "$prd" ]; then
|
|
7045
|
-
echo "Loki Mode - Resume iteration #$iteration (retry #$retry). PRD: $prd. $human_directive $queue_tasks $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $compaction_reminder $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
7072
|
+
echo "Loki Mode - Resume iteration #$iteration (retry #$retry). PRD: $prd. $human_directive $queue_tasks $bmad_context $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $compaction_reminder $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
7046
7073
|
else
|
|
7047
|
-
echo "Loki Mode - Resume iteration #$iteration (retry #$retry). $human_directive $queue_tasks $checklist_status $app_runner_info $playwright_info $memory_context_section Use .loki/generated-prd.md if exists. $rarv_instruction $memory_instruction $compaction_reminder $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
7074
|
+
echo "Loki Mode - Resume iteration #$iteration (retry #$retry). $human_directive $queue_tasks $bmad_context $checklist_status $app_runner_info $playwright_info $memory_context_section Use .loki/generated-prd.md if exists. $rarv_instruction $memory_instruction $compaction_reminder $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
7048
7075
|
fi
|
|
7049
7076
|
fi
|
|
7050
7077
|
}
|
|
7051
7078
|
|
|
7079
|
+
#===============================================================================
|
|
7080
|
+
# BMAD Task Queue Population
|
|
7081
|
+
#===============================================================================
|
|
7082
|
+
|
|
7083
|
+
# Populate the task queue from BMAD epic/story artifacts
|
|
7084
|
+
# Only runs once -- skips if queue was already populated from BMAD
|
|
7085
|
+
populate_bmad_queue() {
|
|
7086
|
+
# Skip if no BMAD tasks file
|
|
7087
|
+
if [[ ! -f ".loki/bmad-tasks.json" ]]; then
|
|
7088
|
+
return 0
|
|
7089
|
+
fi
|
|
7090
|
+
|
|
7091
|
+
# Skip if already populated (marker file)
|
|
7092
|
+
if [[ -f ".loki/queue/.bmad-populated" ]]; then
|
|
7093
|
+
log_info "BMAD queue already populated, skipping"
|
|
7094
|
+
return 0
|
|
7095
|
+
fi
|
|
7096
|
+
|
|
7097
|
+
log_step "Populating task queue from BMAD stories..."
|
|
7098
|
+
|
|
7099
|
+
# Ensure queue directory exists
|
|
7100
|
+
mkdir -p ".loki/queue"
|
|
7101
|
+
|
|
7102
|
+
# Read BMAD tasks and create queue entries
|
|
7103
|
+
python3 << 'BMAD_QUEUE_EOF' 2>/dev/null
|
|
7104
|
+
import json
|
|
7105
|
+
import os
|
|
7106
|
+
import sys
|
|
7107
|
+
|
|
7108
|
+
bmad_tasks_path = ".loki/bmad-tasks.json"
|
|
7109
|
+
pending_path = ".loki/queue/pending.json"
|
|
7110
|
+
|
|
7111
|
+
try:
|
|
7112
|
+
with open(bmad_tasks_path, "r") as f:
|
|
7113
|
+
bmad_data = json.load(f)
|
|
7114
|
+
except (json.JSONDecodeError, FileNotFoundError) as e:
|
|
7115
|
+
print(f"Warning: Could not read BMAD tasks: {e}", file=sys.stderr)
|
|
7116
|
+
sys.exit(0)
|
|
7117
|
+
|
|
7118
|
+
# Extract stories from BMAD structure
|
|
7119
|
+
# Supports both flat list and nested epic/story format
|
|
7120
|
+
stories = []
|
|
7121
|
+
if isinstance(bmad_data, list):
|
|
7122
|
+
stories = bmad_data
|
|
7123
|
+
elif isinstance(bmad_data, dict):
|
|
7124
|
+
# Handle {"epics": [...]} or {"tasks": [...]} formats
|
|
7125
|
+
for key in ("epics", "tasks", "stories"):
|
|
7126
|
+
if key in bmad_data:
|
|
7127
|
+
items = bmad_data[key]
|
|
7128
|
+
if isinstance(items, list):
|
|
7129
|
+
for item in items:
|
|
7130
|
+
if isinstance(item, dict) and "stories" in item:
|
|
7131
|
+
# Epic with nested stories
|
|
7132
|
+
epic_name = item.get("title", item.get("name", ""))
|
|
7133
|
+
for story in item["stories"]:
|
|
7134
|
+
if isinstance(story, dict):
|
|
7135
|
+
story.setdefault("epic", epic_name)
|
|
7136
|
+
stories.append(story)
|
|
7137
|
+
else:
|
|
7138
|
+
stories.append(item)
|
|
7139
|
+
break
|
|
7140
|
+
|
|
7141
|
+
if not stories:
|
|
7142
|
+
print("No BMAD stories found to queue", file=sys.stderr)
|
|
7143
|
+
sys.exit(0)
|
|
7144
|
+
|
|
7145
|
+
# Load existing pending tasks (if any)
|
|
7146
|
+
existing = []
|
|
7147
|
+
if os.path.exists(pending_path):
|
|
7148
|
+
try:
|
|
7149
|
+
with open(pending_path, "r") as f:
|
|
7150
|
+
data = json.load(f)
|
|
7151
|
+
if isinstance(data, list):
|
|
7152
|
+
existing = data
|
|
7153
|
+
elif isinstance(data, dict) and "tasks" in data:
|
|
7154
|
+
existing = data["tasks"]
|
|
7155
|
+
except (json.JSONDecodeError, FileNotFoundError):
|
|
7156
|
+
existing = []
|
|
7157
|
+
|
|
7158
|
+
# Convert BMAD stories to queue task format
|
|
7159
|
+
for i, story in enumerate(stories):
|
|
7160
|
+
if not isinstance(story, dict):
|
|
7161
|
+
continue
|
|
7162
|
+
task = {
|
|
7163
|
+
"id": f"bmad-{i+1}",
|
|
7164
|
+
"title": story.get("title", story.get("name", f"BMAD Story {i+1}")),
|
|
7165
|
+
"description": story.get("description", story.get("action", "")),
|
|
7166
|
+
"priority": story.get("priority", "medium"),
|
|
7167
|
+
"source": "bmad",
|
|
7168
|
+
}
|
|
7169
|
+
epic = story.get("epic", "")
|
|
7170
|
+
if epic:
|
|
7171
|
+
task["epic"] = epic
|
|
7172
|
+
acceptance = story.get("acceptance_criteria", story.get("criteria", []))
|
|
7173
|
+
if acceptance:
|
|
7174
|
+
task["acceptance_criteria"] = acceptance
|
|
7175
|
+
existing.append(task)
|
|
7176
|
+
|
|
7177
|
+
# Write updated pending queue
|
|
7178
|
+
with open(pending_path, "w") as f:
|
|
7179
|
+
json.dump(existing, f, indent=2)
|
|
7180
|
+
|
|
7181
|
+
print(f"Added {len(stories)} BMAD stories to task queue")
|
|
7182
|
+
BMAD_QUEUE_EOF
|
|
7183
|
+
|
|
7184
|
+
if [[ $? -ne 0 ]]; then
|
|
7185
|
+
log_warn "Failed to populate BMAD queue (python3 error)"
|
|
7186
|
+
return 0
|
|
7187
|
+
fi
|
|
7188
|
+
|
|
7189
|
+
# Mark as populated so we don't re-add on restart
|
|
7190
|
+
touch ".loki/queue/.bmad-populated"
|
|
7191
|
+
log_info "BMAD queue population complete"
|
|
7192
|
+
}
|
|
7193
|
+
|
|
7052
7194
|
#===============================================================================
|
|
7053
7195
|
# Main Autonomous Loop
|
|
7054
7196
|
#===============================================================================
|
|
@@ -7130,6 +7272,9 @@ run_autonomous() {
|
|
|
7130
7272
|
fi
|
|
7131
7273
|
fi
|
|
7132
7274
|
|
|
7275
|
+
# Populate task queue from BMAD artifacts (if present, runs once)
|
|
7276
|
+
populate_bmad_queue
|
|
7277
|
+
|
|
7133
7278
|
# Check max iterations before starting
|
|
7134
7279
|
if check_max_iterations; then
|
|
7135
7280
|
log_error "Max iterations already reached. Reset with: rm .loki/autonomy-state.json"
|
package/dashboard/__init__.py
CHANGED