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/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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """PRD Quality Analyzer for Loki Mode v5.44.0
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"
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "6.0.0"
10
+ __version__ = "6.1.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v6.0.0
5
+ **Version:** v6.1.0
6
6
 
7
7
  ---
8
8