loki-mode 6.1.0 → 6.2.1

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 CHANGED
@@ -9,7 +9,7 @@
9
9
  [![Agent Types](https://img.shields.io/badge/Agent%20Types-41-blue)]()
10
10
  [![Autonomi](https://img.shields.io/badge/Autonomi-autonomi.dev-5B4EEA)](https://www.autonomi.dev/)
11
11
 
12
- **Current Version: v5.52.4**
12
+ **Current Version: v6.2.1**
13
13
 
14
14
  ---
15
15
 
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes PRD to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v6.1.0
6
+ # Loki Mode v6.2.1
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -263,4 +263,4 @@ The following features are documented in skill modules but not yet fully automat
263
263
  | Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
264
264
  | Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
265
265
 
266
- **v6.1.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
266
+ **v6.2.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 6.1.0
1
+ 6.2.1
@@ -80,7 +80,7 @@ def parse_frontmatter(text: str) -> Tuple[Dict[str, Any], str]:
80
80
  """Extract YAML frontmatter from a markdown document.
81
81
 
82
82
  Returns (metadata_dict, body_without_frontmatter).
83
- Handles simple YAML: scalars, lists (flow and block), quoted strings.
83
+ Handles simple YAML: scalars, lists (flow-style only; block-style not supported), quoted strings.
84
84
  Does NOT require PyYAML -- uses regex-based extraction.
85
85
  """
86
86
  stripped = text.lstrip()
@@ -514,9 +514,10 @@ def validate_chain(
514
514
  "message": "PRD frontmatter has no inputDocuments -- cannot verify product-brief linkage.",
515
515
  })
516
516
  else:
517
+ docs = input_docs if isinstance(input_docs, list) else [input_docs] if input_docs else []
517
518
  findings.append({
518
519
  "level": "info",
519
- "message": f"PRD references input documents: {', '.join(input_docs)}",
520
+ "message": f"PRD references input documents: {', '.join(docs)}",
520
521
  })
521
522
 
522
523
  # 2. Missing artifacts
package/autonomy/loki CHANGED
@@ -448,6 +448,12 @@ show_help() {
448
448
  echo " --parallel Enable parallel mode with git worktrees"
449
449
  echo " --budget USD Set cost budget limit"
450
450
  echo ""
451
+ echo "Progressive Isolation (for 'run'):"
452
+ echo " --worktree, -w Git worktree isolation (separate branch)"
453
+ echo " --pr Auto-create PR after completion (implies --worktree)"
454
+ echo " --ship Auto-merge after PR (implies --pr)"
455
+ echo " --detach, -d Run in background (implies --worktree)"
456
+ echo ""
451
457
  echo "Examples:"
452
458
  echo " loki run 123 # GitHub issue from current repo"
453
459
  echo " loki run PROJ-456 # Jira issue"
@@ -640,8 +646,9 @@ cmd_start() {
640
646
  if [[ -n "$bmad_project_path" ]]; then
641
647
  # Resolve to absolute path
642
648
  if [[ ! "$bmad_project_path" = /* ]]; then
649
+ local original_bmad_path="$bmad_project_path"
643
650
  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}"
651
+ echo -e "${RED}Error: BMAD project path does not exist: $original_bmad_path${NC}"
645
652
  exit 1
646
653
  }
647
654
  fi
@@ -2519,6 +2526,10 @@ cmd_run() {
2519
2526
  local start_args=()
2520
2527
  local no_start=false
2521
2528
  local provider_override=""
2529
+ local use_worktree=false
2530
+ local create_pr=false
2531
+ local auto_merge=false
2532
+ local run_detached=false
2522
2533
 
2523
2534
  # Parse arguments
2524
2535
  while [[ $# -gt 0 ]]; do
@@ -2551,6 +2562,14 @@ cmd_run() {
2551
2562
  echo " --sandbox Run in Docker sandbox"
2552
2563
  echo " --budget USD Set cost budget limit"
2553
2564
  echo ""
2565
+ echo "Progressive Isolation:"
2566
+ echo " --worktree, -w Git worktree isolation (separate branch)"
2567
+ echo " --pr Worktree + auto-create PR (implies --worktree)"
2568
+ echo " --ship Worktree + PR + auto-merge (implies --pr)"
2569
+ echo " --detach, -d Run in background (implies --worktree)"
2570
+ echo ""
2571
+ echo " Cascade: --ship implies --pr implies --worktree"
2572
+ echo ""
2554
2573
  echo "Environment Variables:"
2555
2574
  echo " JIRA_API_TOKEN Jira API token (for Jira issues)"
2556
2575
  echo " JIRA_URL Jira base URL (for Jira issues)"
@@ -2563,6 +2582,10 @@ cmd_run() {
2563
2582
  echo " loki run https://gitlab.com/o/r/-/issues/42 # GitLab issue"
2564
2583
  echo " loki run 123 --dry-run # Preview PRD"
2565
2584
  echo " loki run 123 --parallel --provider codex # Parallel mode with Codex"
2585
+ echo " loki run 123 --worktree # Isolated branch"
2586
+ echo " loki run 123 --pr # Auto-create PR"
2587
+ echo " loki run 123 --ship # Full automation: PR + merge"
2588
+ echo " loki run 123 --ship -d # Background, full automation"
2566
2589
  exit 0
2567
2590
  ;;
2568
2591
  --dry-run)
@@ -2626,6 +2649,30 @@ cmd_run() {
2626
2649
  start_args+=("--budget" "${1#*=}")
2627
2650
  shift
2628
2651
  ;;
2652
+ --worktree|-w)
2653
+ start_args+=("--parallel")
2654
+ use_worktree=true
2655
+ shift
2656
+ ;;
2657
+ --pr)
2658
+ use_worktree=true
2659
+ create_pr=true
2660
+ start_args+=("--parallel")
2661
+ shift
2662
+ ;;
2663
+ --ship)
2664
+ use_worktree=true
2665
+ create_pr=true
2666
+ auto_merge=true
2667
+ start_args+=("--parallel")
2668
+ shift
2669
+ ;;
2670
+ --detach|-d)
2671
+ use_worktree=true
2672
+ run_detached=true
2673
+ start_args+=("--parallel")
2674
+ shift
2675
+ ;;
2629
2676
  -*)
2630
2677
  echo -e "${RED}Unknown option: $1${NC}"
2631
2678
  echo "Run 'loki run --help' for usage."
@@ -2687,10 +2734,66 @@ cmd_run() {
2687
2734
  fi
2688
2735
  echo ""
2689
2736
 
2737
+ # Progressive isolation: set up worktree branch naming
2738
+ if $use_worktree; then
2739
+ local branch_name="issue/${issue_provider}-${number:-$(date +%s)}"
2740
+ log_info "Progressive isolation: branch $branch_name"
2741
+ export LOKI_PARALLEL_MODE=true
2742
+ export LOKI_WORKTREE_BRANCH="$branch_name"
2743
+ fi
2744
+
2690
2745
  # Generate PRD
2691
2746
  local prd_content
2692
2747
  prd_content=$(echo "$issue_json" | generate_prd_from_issue)
2693
2748
 
2749
+ # Detached mode: fork to background
2750
+ if $run_detached; then
2751
+ local log_file="$LOKI_DIR/logs/run-${number:-$(date +%s)}.log"
2752
+ mkdir -p "$(dirname "$log_file")"
2753
+ echo -e "${GREEN}Running detached. Logs: $log_file${NC}"
2754
+ echo -e "Check status: ${CYAN}loki status${NC}"
2755
+
2756
+ # Write PRD first so background process can use it
2757
+ mkdir -p "$LOKI_DIR"
2758
+ local detach_prd="$LOKI_DIR/prd-issue-${number}.md"
2759
+ echo "$prd_content" > "$detach_prd"
2760
+
2761
+ if [[ -z "${branch_name:-}" ]]; then
2762
+ branch_name="issue/detach-$(date +%s)"
2763
+ fi
2764
+
2765
+ nohup bash -c "
2766
+ cd $(pwd)
2767
+ export LOKI_DETACHED=true
2768
+ export LOKI_PARALLEL_MODE=true
2769
+ export LOKI_WORKTREE_BRANCH=\"$branch_name\"
2770
+ $(command -v loki || echo "$0") start \"$detach_prd\" --parallel ${start_args[*]+"${start_args[*]}"}
2771
+
2772
+ # Post-completion: create PR if requested
2773
+ if [[ \"$create_pr\" == \"true\" ]]; then
2774
+ branch_current=\$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo \"\")
2775
+ if [[ -n \"\$branch_current\" && \"\$branch_current\" != \"main\" && \"\$branch_current\" != \"master\" ]]; then
2776
+ git push origin \"\$branch_current\" 2>/dev/null || true
2777
+ gh pr create --title \"${title:-Implementation for issue ${issue_ref}}\" --body \"Implemented by Loki Mode\" --head \"\$branch_current\" 2>/dev/null || true
2778
+ fi
2779
+ fi
2780
+ # Post-completion: auto-merge if requested
2781
+ if [[ \"$auto_merge\" == \"true\" ]]; then
2782
+ branch_current=\$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo \"\")
2783
+ if gh pr merge \"\$branch_current\" --squash --delete-branch 2>/dev/null; then
2784
+ if [[ -n \"${number:-}\" ]]; then
2785
+ gh issue close \"$number\" --comment \"Resolved by Loki Mode\" 2>/dev/null || true
2786
+ fi
2787
+ fi
2788
+ fi
2789
+ " > "$log_file" 2>&1 &
2790
+
2791
+ local bg_pid=$!
2792
+ echo "$bg_pid" > "$LOKI_DIR/run-${number:-detached}.pid"
2793
+ echo "Background PID: $bg_pid"
2794
+ return 0
2795
+ fi
2796
+
2694
2797
  # Handle dry-run
2695
2798
  if [[ "$dry_run" == "true" ]]; then
2696
2799
  echo -e "${BOLD}Generated PRD Preview:${NC}"
@@ -2728,6 +2831,77 @@ cmd_run() {
2728
2831
  echo ""
2729
2832
  echo -e "${GREEN}Starting Loki Mode with generated PRD...${NC}"
2730
2833
  cmd_start "$output_file" ${start_args[@]+"${start_args[@]}"}
2834
+
2835
+ # Progressive isolation: create PR
2836
+ if $create_pr; then
2837
+ echo ""
2838
+ echo -e "${GREEN}Creating pull request...${NC}"
2839
+ local pr_title="${title:-Implementation for issue ${issue_ref}}"
2840
+ local pr_body="Implemented by Loki Mode (autonomous agent)
2841
+
2842
+ Issue: ${issue_ref}
2843
+ Provider: ${issue_provider}
2844
+
2845
+ ## Changes
2846
+ $(git log --oneline "main..HEAD" 2>/dev/null || echo "See diff")"
2847
+
2848
+ case "${issue_provider:-github}" in
2849
+ github)
2850
+ if command -v gh &>/dev/null; then
2851
+ local branch_current
2852
+ branch_current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
2853
+ if [[ -n "$branch_current" && "$branch_current" != "main" && "$branch_current" != "master" ]]; then
2854
+ git push origin "$branch_current" 2>/dev/null || true
2855
+ gh pr create --title "$pr_title" --body "$pr_body" --head "$branch_current" 2>/dev/null && \
2856
+ echo -e "${GREEN}PR created${NC}" || \
2857
+ echo -e "${YELLOW}PR creation failed${NC}"
2858
+ fi
2859
+ fi
2860
+ ;;
2861
+ gitlab)
2862
+ if command -v glab &>/dev/null; then
2863
+ local branch_current
2864
+ branch_current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
2865
+ if [[ -n "$branch_current" && "$branch_current" != "main" && "$branch_current" != "master" ]]; then
2866
+ git push origin "$branch_current" 2>/dev/null || true
2867
+ glab mr create --title "$pr_title" --description "$pr_body" --source-branch "$branch_current" 2>/dev/null && \
2868
+ echo -e "${GREEN}MR created${NC}" || \
2869
+ echo -e "${YELLOW}MR creation failed${NC}"
2870
+ fi
2871
+ fi
2872
+ ;;
2873
+ *)
2874
+ echo -e "${YELLOW}PR creation not supported for provider: $issue_provider${NC}"
2875
+ ;;
2876
+ esac
2877
+ fi
2878
+
2879
+ # Progressive isolation: auto-merge
2880
+ if $auto_merge; then
2881
+ echo -e "${GREEN}Auto-merging...${NC}"
2882
+ case "${issue_provider:-github}" in
2883
+ github)
2884
+ local branch_current
2885
+ branch_current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
2886
+ if gh pr merge "$branch_current" --squash --delete-branch 2>/dev/null; then
2887
+ echo -e "${GREEN}PR merged and branch deleted${NC}"
2888
+ if [[ -n "${number:-}" ]]; then
2889
+ gh issue close "$number" --comment "Resolved by Loki Mode" 2>/dev/null || true
2890
+ fi
2891
+ else
2892
+ echo -e "${YELLOW}Auto-merge failed. PR remains open.${NC}"
2893
+ fi
2894
+ ;;
2895
+ gitlab)
2896
+ glab mr merge --squash --remove-source-branch 2>/dev/null && \
2897
+ echo -e "${GREEN}MR merged and branch deleted${NC}" || \
2898
+ echo -e "${YELLOW}Auto-merge failed. MR remains open.${NC}"
2899
+ ;;
2900
+ *)
2901
+ echo -e "${YELLOW}Auto-merge not supported for provider: $issue_provider${NC}"
2902
+ ;;
2903
+ esac
2904
+ fi
2731
2905
  fi
2732
2906
  }
2733
2907
 
@@ -6739,6 +6913,169 @@ cmd_migrate() {
6739
6913
  cmd_migrate_start "$codebase_path" "$target" "$plan_only" "$phase" "$parallel" "$compliance" "$dry_run" "$do_resume" "$multi_repo" "$export_report" "$no_dashboard" "$no_docs"
6740
6914
  }
6741
6915
 
6916
+ #===============================================================================
6917
+ # loki cluster - Custom workflow templates (v6.2.0)
6918
+ #===============================================================================
6919
+
6920
+ cmd_cluster() {
6921
+ local subcmd="${1:-list}"
6922
+ shift 2>/dev/null || true
6923
+
6924
+ case "$subcmd" in
6925
+ --help|-h|help)
6926
+ echo -e "${BOLD}loki cluster${NC} - Custom workflow templates (v6.2.0)"
6927
+ echo ""
6928
+ echo "Usage: loki cluster <command> [options]"
6929
+ echo ""
6930
+ echo "Commands:"
6931
+ echo " list List available workflow templates"
6932
+ echo " validate <name> Validate a cluster template"
6933
+ echo " run <name> [args] Execute a cluster workflow"
6934
+ echo " info <name> Show template details"
6935
+ echo ""
6936
+ echo "Examples:"
6937
+ echo " loki cluster list"
6938
+ echo " loki cluster validate security-review"
6939
+ echo " loki cluster info code-review"
6940
+ ;;
6941
+ list)
6942
+ echo -e "${BOLD}Available Cluster Templates${NC}"
6943
+ echo ""
6944
+ local template_dir="$SKILL_DIR/templates/clusters"
6945
+ if [[ ! -d "$template_dir" ]]; then
6946
+ echo "No templates found at $template_dir"
6947
+ return 1
6948
+ fi
6949
+ for f in "$template_dir"/*.json; do
6950
+ [[ ! -f "$f" ]] && continue
6951
+ local name
6952
+ name=$(basename "$f" .json)
6953
+ local desc
6954
+ desc=$(python3 -c "
6955
+ import json, sys
6956
+ try:
6957
+ with open(sys.argv[1]) as f:
6958
+ d = json.load(f)
6959
+ print(d.get('description', 'No description'))
6960
+ except: print('Error reading template')
6961
+ " "$f" 2>/dev/null || echo "")
6962
+ local agent_count
6963
+ agent_count=$(python3 -c "
6964
+ import json, sys
6965
+ try:
6966
+ with open(sys.argv[1]) as f:
6967
+ d = json.load(f)
6968
+ print(len(d.get('agents', [])))
6969
+ except: print('?')
6970
+ " "$f" 2>/dev/null || echo "?")
6971
+ printf " %-20s %s agents %s\n" "$name" "$agent_count" "$desc"
6972
+ done
6973
+ ;;
6974
+ validate)
6975
+ local template_name="${1:-}"
6976
+ if [[ -z "$template_name" ]]; then
6977
+ echo -e "${RED}Usage: loki cluster validate <template-name>${NC}"
6978
+ return 1
6979
+ fi
6980
+ local template_file="$SKILL_DIR/templates/clusters/${template_name}.json"
6981
+ if [[ ! -f "$template_file" ]]; then
6982
+ echo -e "${RED}Template not found: $template_name${NC}"
6983
+ echo "Run 'loki cluster list' to see available templates."
6984
+ return 1
6985
+ fi
6986
+ echo -e "${CYAN}Validating: $template_name${NC}"
6987
+ local errors
6988
+ errors=$(python3 -c "
6989
+ import json, sys
6990
+ sys.path.insert(0, '$SKILL_DIR')
6991
+ from swarm.patterns import TopologyValidator
6992
+ errors = TopologyValidator.validate_file(sys.argv[1])
6993
+ if errors:
6994
+ for e in errors:
6995
+ print(f'ERROR: {e}')
6996
+ else:
6997
+ print('VALID')
6998
+ " "$template_file" 2>&1)
6999
+ if echo "$errors" | grep -q "^VALID$"; then
7000
+ echo -e "${GREEN}Template is valid${NC}"
7001
+ return 0
7002
+ else
7003
+ echo -e "${RED}Validation errors:${NC}"
7004
+ echo "$errors"
7005
+ return 1
7006
+ fi
7007
+ ;;
7008
+ info)
7009
+ local template_name="${1:-}"
7010
+ if [[ -z "$template_name" ]]; then
7011
+ echo -e "${RED}Usage: loki cluster info <template-name>${NC}"
7012
+ return 1
7013
+ fi
7014
+ local template_file="$SKILL_DIR/templates/clusters/${template_name}.json"
7015
+ if [[ ! -f "$template_file" ]]; then
7016
+ echo -e "${RED}Template not found: $template_name${NC}"
7017
+ return 1
7018
+ fi
7019
+ python3 -c "
7020
+ import json, sys
7021
+ with open(sys.argv[1]) as f:
7022
+ d = json.load(f)
7023
+ print(f\"Name: {d.get('name', 'unknown')}\")
7024
+ print(f\"Description: {d.get('description', 'none')}\")
7025
+ print(f\"Version: {d.get('version', 'unknown')}\")
7026
+ print(f\"Topology: {d.get('topology', 'unknown')}\")
7027
+ print(f\"Agents: {len(d.get('agents', []))}\")
7028
+ print()
7029
+ for a in d.get('agents', []):
7030
+ subs = ', '.join(a.get('subscribes', []))
7031
+ pubs = ', '.join(a.get('publishes', []))
7032
+ print(f\" [{a['id']}] ({a.get('type', '?')})\")
7033
+ print(f\" Role: {a.get('role', '?')}\")
7034
+ print(f\" Subscribes: {subs}\")
7035
+ print(f\" Publishes: {pubs}\")
7036
+ print()
7037
+ " "$template_file"
7038
+ ;;
7039
+ run)
7040
+ local template_name="${1:-}"
7041
+ if [[ -z "$template_name" ]]; then
7042
+ echo -e "${RED}Usage: loki cluster run <template-name> [args]${NC}"
7043
+ return 1
7044
+ fi
7045
+ local template_file="$SKILL_DIR/templates/clusters/${template_name}.json"
7046
+ if [[ ! -f "$template_file" ]]; then
7047
+ echo -e "${RED}Template not found: $template_name${NC}"
7048
+ return 1
7049
+ fi
7050
+ # Validate first
7051
+ local result
7052
+ result=$(python3 -c "
7053
+ import json, sys
7054
+ sys.path.insert(0, '$SKILL_DIR')
7055
+ from swarm.patterns import TopologyValidator
7056
+ errors = TopologyValidator.validate_file(sys.argv[1])
7057
+ if errors:
7058
+ for e in errors: print(f'ERROR: {e}')
7059
+ else:
7060
+ print('VALID')
7061
+ " "$template_file" 2>&1)
7062
+ if ! echo "$result" | grep -q "^VALID$"; then
7063
+ echo -e "${RED}Template validation failed:${NC}"
7064
+ echo "$result"
7065
+ return 1
7066
+ fi
7067
+ echo -e "${GREEN}Cluster template: $template_name${NC}"
7068
+ echo -e "${YELLOW}Note: Cluster execution engine is planned for v6.3.0.${NC}"
7069
+ echo -e "Template validated successfully. Use 'loki cluster info $template_name' for details."
7070
+ ;;
7071
+ *)
7072
+ echo -e "${RED}Unknown cluster command: $subcmd${NC}"
7073
+ echo "Run 'loki cluster --help' for usage."
7074
+ return 1
7075
+ ;;
7076
+ esac
7077
+ }
7078
+
6742
7079
  # Main command dispatcher
6743
7080
  main() {
6744
7081
  if [ $# -eq 0 ]; then
@@ -6869,6 +7206,9 @@ main() {
6869
7206
  migrate)
6870
7207
  cmd_migrate "$@"
6871
7208
  ;;
7209
+ cluster)
7210
+ cmd_cluster "$@"
7211
+ ;;
6872
7212
  metrics)
6873
7213
  cmd_metrics "$@"
6874
7214
  ;;
@@ -114,7 +114,6 @@ DIMENSIONS = {
114
114
  "weight": 0.75,
115
115
  "heading_patterns": [
116
116
  r"(?i)#+\s.*(?:deploy|hosting|infra|ci.?cd|environment)",
117
- r"(?i)^##\s+Non-Functional\s+Requirements",
118
117
  ],
119
118
  "content_patterns": [
120
119
  r"(?i)\b(?:deploy|hosting|ci.?cd|pipeline|staging|production)\b",
@@ -259,8 +258,9 @@ class PrdAnalyzer:
259
258
  if in_feature_section and re.match(r"^\s*#+\s", line):
260
259
  in_feature_section = False
261
260
  continue
262
- if re.match(r"^\s*[-*]\s+\S", line) or re.match(r"^\s*\d+\.\s+\S", line):
263
- count += 1
261
+ if in_feature_section:
262
+ if re.match(r"^\s*[-*]\s+\S", line) or re.match(r"^\s*\d+\.\s+\S", line):
263
+ count += 1
264
264
 
265
265
  self.feature_count = count
266
266
  for threshold, label in SCOPE_THRESHOLDS:
package/autonomy/run.sh CHANGED
@@ -7043,7 +7043,19 @@ except: pass
7043
7043
  fi
7044
7044
  local bmad_tasks=""
7045
7045
  if [[ -f ".loki/bmad-tasks.json" ]]; then
7046
- bmad_tasks=$(head -c 32000 ".loki/bmad-tasks.json")
7046
+ bmad_tasks=$(python3 -c "
7047
+ import json, sys
7048
+ try:
7049
+ with open('.loki/bmad-tasks.json') as f:
7050
+ data = json.load(f)
7051
+ out = json.dumps(data, indent=None)
7052
+ if len(out) > 32000 and isinstance(data, list):
7053
+ while len(json.dumps(data, indent=None)) > 32000 and data:
7054
+ data.pop()
7055
+ out = json.dumps(data, indent=None)
7056
+ print(out[:32000])
7057
+ except: pass
7058
+ " 2>/dev/null)
7047
7059
  fi
7048
7060
  local bmad_validation=""
7049
7061
  if [[ -f ".loki/bmad-validation.md" ]]; then
@@ -7100,7 +7112,7 @@ populate_bmad_queue() {
7100
7112
  mkdir -p ".loki/queue"
7101
7113
 
7102
7114
  # Read BMAD tasks and create queue entries
7103
- python3 << 'BMAD_QUEUE_EOF' 2>/dev/null
7115
+ python3 << 'BMAD_QUEUE_EOF'
7104
7116
  import json
7105
7117
  import os
7106
7118
  import sys
@@ -7155,12 +7167,16 @@ if os.path.exists(pending_path):
7155
7167
  except (json.JSONDecodeError, FileNotFoundError):
7156
7168
  existing = []
7157
7169
 
7158
- # Convert BMAD stories to queue task format
7170
+ # Convert BMAD stories to queue task format (with deduplication)
7171
+ existing_ids = {t.get("id") for t in existing if isinstance(t, dict)}
7159
7172
  for i, story in enumerate(stories):
7160
7173
  if not isinstance(story, dict):
7161
7174
  continue
7175
+ task_id = f"bmad-{i+1}"
7176
+ if task_id in existing_ids:
7177
+ continue
7162
7178
  task = {
7163
- "id": f"bmad-{i+1}",
7179
+ "id": task_id,
7164
7180
  "title": story.get("title", story.get("name", f"BMAD Story {i+1}")),
7165
7181
  "description": story.get("description", story.get("action", "")),
7166
7182
  "priority": story.get("priority", "medium"),
@@ -62,6 +62,35 @@ DASHBOARD_PORT="${LOKI_DASHBOARD_PORT:-57374}"
62
62
  # Security: Prompt injection disabled by default for enterprise security
63
63
  PROMPT_INJECTION_ENABLED="${LOKI_PROMPT_INJECTION:-false}"
64
64
 
65
+ #===============================================================================
66
+ # Docker Credential Mount Presets
67
+ #===============================================================================
68
+
69
+ # Preset definitions: "host_path:container_path:mode"
70
+ # Container runs as user 'loki' (UID 1000), so mount to /home/loki/
71
+ declare -A DOCKER_MOUNT_PRESETS
72
+ DOCKER_MOUNT_PRESETS=(
73
+ # Note: gh and git are also hardcoded in start_sandbox() defaults
74
+ [gh]="$HOME/.config/gh:/home/loki/.config/gh:ro"
75
+ [git]="$HOME/.gitconfig:/home/loki/.gitconfig:ro"
76
+ [ssh]="$HOME/.ssh/known_hosts:/home/loki/.ssh/known_hosts:ro"
77
+ [aws]="$HOME/.aws:/home/loki/.aws:ro"
78
+ [azure]="$HOME/.azure:/home/loki/.azure:ro"
79
+ [kube]="$HOME/.kube:/home/loki/.kube:ro"
80
+ [terraform]="$HOME/.terraform.d:/home/loki/.terraform.d:ro"
81
+ [gcloud]="$HOME/.config/gcloud:/home/loki/.config/gcloud:ro"
82
+ [npm]="$HOME/.npmrc:/home/loki/.npmrc:ro"
83
+ )
84
+
85
+ # Environment variables auto-passed per preset (comma-separated)
86
+ declare -A DOCKER_ENV_PRESETS
87
+ DOCKER_ENV_PRESETS=(
88
+ [aws]="AWS_REGION,AWS_PROFILE,AWS_DEFAULT_REGION"
89
+ [azure]="AZURE_SUBSCRIPTION_ID,AZURE_TENANT_ID"
90
+ [gcloud]="GOOGLE_PROJECT,GOOGLE_REGION,GCLOUD_PROJECT"
91
+ [terraform]="TF_VAR_*"
92
+ )
93
+
65
94
  #===============================================================================
66
95
  # Utility Functions
67
96
  #===============================================================================
@@ -767,9 +796,133 @@ docker_desktop_sandbox_run() {
767
796
  # Docker Container Sandbox (standard Docker - fallback from Docker Desktop)
768
797
  #===============================================================================
769
798
 
799
+ # Resolve Docker credential mount presets
800
+ # Reads from: .loki/config/settings.json dockerMounts, LOKI_DOCKER_MOUNTS env var
801
+ # Returns: string of -v and -e flags for docker run
802
+ resolve_docker_mounts() {
803
+ RESOLVED_MOUNTS=()
804
+
805
+ # Read configured presets (JSON array of strings)
806
+ local presets_json=""
807
+ if [[ -f "${PROJECT_DIR}/.loki/config/settings.json" ]]; then
808
+ presets_json=$(python3 -c "
809
+ import json, sys
810
+ try:
811
+ with open(sys.argv[1] + '/.loki/config/settings.json') as f:
812
+ print(json.dumps(json.load(f).get('dockerMounts', [])))
813
+ except: print('[]')
814
+ " "${PROJECT_DIR}" 2>/dev/null || echo "[]")
815
+ fi
816
+
817
+ # Override with env var if set
818
+ if [[ -n "${LOKI_DOCKER_MOUNTS:-}" ]]; then
819
+ presets_json="$LOKI_DOCKER_MOUNTS"
820
+ fi
821
+
822
+ # Parse and resolve
823
+ if [[ "$presets_json" != "[]" ]] && [[ "$presets_json" != "" ]]; then
824
+ local preset_names
825
+ preset_names=$(python3 -c "
826
+ import json, sys
827
+ try:
828
+ data = json.loads(sys.argv[1])
829
+ if isinstance(data, list):
830
+ for item in data:
831
+ print(item)
832
+ except: pass
833
+ " "$presets_json" 2>/dev/null || echo "")
834
+
835
+ local name
836
+ while IFS= read -r name; do
837
+ [[ -z "$name" ]] && continue
838
+
839
+ local preset_value="${DOCKER_MOUNT_PRESETS[$name]:-}"
840
+ if [[ -z "$preset_value" ]]; then
841
+ log_warn "Unknown Docker mount preset: $name"
842
+ continue
843
+ fi
844
+
845
+ IFS=':' read -ra parts <<< "$preset_value"
846
+ local host_path="${parts[0]}"
847
+ local container_path="${parts[1]}"
848
+ local mode="${parts[2]:-ro}"
849
+
850
+ # Safe tilde expansion (no eval)
851
+ if [[ "$host_path" == "~/"* ]]; then
852
+ host_path="$HOME/${host_path#\~/}"
853
+ elif [[ "$host_path" == "~" ]]; then
854
+ host_path="$HOME"
855
+ fi
856
+
857
+ # Only mount if host path exists
858
+ if [[ -e "$host_path" ]]; then
859
+ RESOLVED_MOUNTS+=("-v" "${host_path}:${container_path}:${mode}")
860
+ log_info " Mount preset [$name]: $host_path -> $container_path ($mode)"
861
+ fi
862
+
863
+ # Add associated env vars
864
+ local env_list="${DOCKER_ENV_PRESETS[$name]:-}"
865
+ if [[ -n "$env_list" ]]; then
866
+ IFS=',' read -ra env_names <<< "$env_list"
867
+ local env_name
868
+ for env_name in "${env_names[@]}"; do
869
+ if [[ "$env_name" == *"*" ]]; then
870
+ # Wildcard: pass all matching env vars
871
+ local prefix="${env_name%\*}"
872
+ while IFS='=' read -r key val; do
873
+ [[ "$key" == "$prefix"* ]] && [[ -n "$val" ]] && {
874
+ RESOLVED_MOUNTS+=("-e" "$key")
875
+ log_info " Env wildcard [$name]: passing $key"
876
+ }
877
+ done < <(env)
878
+ elif [[ -n "${!env_name:-}" ]]; then
879
+ RESOLVED_MOUNTS+=("-e" "$env_name")
880
+ fi
881
+ done
882
+ fi
883
+ done <<< "$preset_names"
884
+ fi
885
+ }
886
+
770
887
  start_sandbox() {
771
- local prd_path="${1:-}"
888
+ # Parse arguments: extract flags before positional args
889
+ local prd_path=""
772
890
  local provider="${LOKI_PROVIDER:-claude}"
891
+ local no_mounts=false
892
+ local -a custom_mounts=()
893
+
894
+ while [[ $# -gt 0 ]]; do
895
+ case "$1" in
896
+ --mount)
897
+ if [[ -n "${2:-}" ]]; then
898
+ custom_mounts+=("$2")
899
+ shift 2
900
+ else
901
+ log_error "--mount requires HOST:CONTAINER:MODE argument"
902
+ return 1
903
+ fi
904
+ ;;
905
+ --no-mounts)
906
+ no_mounts=true
907
+ shift
908
+ ;;
909
+ --provider)
910
+ if [[ -n "${2:-}" ]]; then
911
+ provider="$2"
912
+ shift 2
913
+ else
914
+ shift
915
+ fi
916
+ ;;
917
+ *)
918
+ # First non-flag argument is the PRD path
919
+ if [[ -z "$prd_path" ]]; then
920
+ prd_path="$1"
921
+ fi
922
+ shift
923
+ ;;
924
+ esac
925
+ done
773
926
 
774
927
  # Set up signal handler to cleanup on Ctrl+C
775
928
  trap cleanup_container INT TERM
@@ -887,6 +1040,41 @@ start_sandbox() {
887
1040
  docker_args+=("--volume" "$HOME/.config/gh:/home/loki/.config/gh:ro")
888
1041
  fi
889
1042
 
1043
+ # Apply Docker credential mount presets (additive on top of defaults above)
1044
+ if [[ "$no_mounts" != "true" ]] && [[ "${LOKI_NO_DOCKER_MOUNTS:-}" != "true" ]]; then
1045
+ resolve_docker_mounts
1046
+ if [[ ${#RESOLVED_MOUNTS[@]} -gt 0 ]]; then
1047
+ docker_args+=("${RESOLVED_MOUNTS[@]}")
1048
+ fi
1049
+ fi
1050
+
1051
+ # Apply custom --mount arguments
1052
+ local custom_mount
1053
+ for custom_mount in "${custom_mounts[@]+"${custom_mounts[@]}"}"; do
1054
+ if [[ -n "$custom_mount" ]]; then
1055
+ IFS=':' read -ra mount_parts <<< "$custom_mount"
1056
+ local c_host="${mount_parts[0]:-}"
1057
+ local c_container="${mount_parts[1]:-}"
1058
+ local c_mode="${mount_parts[2]:-ro}"
1059
+ if [[ -n "$c_host" ]] && [[ -n "$c_container" ]]; then
1060
+ # Safe tilde expansion (no eval)
1061
+ if [[ "$c_host" == "~/"* ]]; then
1062
+ c_host="$HOME/${c_host#\~/}"
1063
+ elif [[ "$c_host" == "~" ]]; then
1064
+ c_host="$HOME"
1065
+ fi
1066
+ if [[ -e "$c_host" ]]; then
1067
+ docker_args+=("--volume" "${c_host}:${c_container}:${c_mode}")
1068
+ log_info " Custom mount: $c_host -> $c_container ($c_mode)"
1069
+ else
1070
+ log_warn "Custom mount source does not exist: $c_host"
1071
+ fi
1072
+ else
1073
+ log_warn "Invalid mount format: $custom_mount (expected HOST:CONTAINER[:MODE])"
1074
+ fi
1075
+ fi
1076
+ done
1077
+
890
1078
  # Pass API keys as environment variables (more secure than mounting files)
891
1079
  if [[ -n "${ANTHROPIC_API_KEY:-}" ]]; then
892
1080
  docker_args+=("--env" "ANTHROPIC_API_KEY")
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "6.1.0"
10
+ __version__ = "6.2.1"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2,33 +2,28 @@
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.1.0
5
+ **Version:** v6.2.1
6
6
 
7
7
  ---
8
8
 
9
- ## What's New in v5.49.1
10
-
11
- ### Enterprise Security (v5.36.0-v5.37.1)
12
- - TLS/HTTPS support for dashboard connections
13
- - OIDC/SSO authentication (Google, Azure AD, Okta)
14
- - RBAC roles (admin, operator, viewer, auditor)
15
- - WebSocket authentication for real-time connections
16
- - Syslog forwarding for SIEM integration
17
- - Non-root Docker with SETUID/SETGID removed
18
- - Salted token hashing and rate limiting
19
-
20
- ### Monitoring & Observability (v5.38.0)
21
- - Prometheus/OpenMetrics `/metrics` endpoint with 9 metrics
22
- - `loki metrics` CLI command
23
- - Agent action audit trail at `.loki/logs/agent-audit.jsonl`
24
- - `loki audit` CLI with log/count subcommands
25
- - SHA-256 chain-hashed tamper-evident audit entries
26
-
27
- ### Workflow Protection (v5.38.0)
28
- - Branch protection: agent sessions auto-create feature branches
29
- - PR creation via `gh` on session completion
30
- - OpenClaw bridge foundation for external integrations
31
- - Network security documentation (Docker/Kubernetes)
9
+ ## What's New in v6.2.1
10
+
11
+ ### Dual-Mode Architecture (v6.0.0)
12
+ - `loki run` command for direct autonomous execution
13
+ - Dual-mode: skill mode (inside Claude Code) and standalone mode
14
+ - Dynamic model resolution across all providers
15
+ - Multi-provider issue fixes and stability improvements
16
+
17
+ ### ChromaDB Semantic Code Search (v6.1.0)
18
+ - Semantic code search via ChromaDB vector database
19
+ - MCP integration with `loki_code_search` and `loki_code_search_stats` tools
20
+ - Automatic codebase indexing with `tools/index-codebase.py`
21
+
22
+ ### Memory System (v5.15.0+)
23
+ - Episodic, semantic, and procedural memory layers
24
+ - Progressive disclosure with 3-layer loading
25
+ - Token economics tracking for discovery vs read tokens
26
+ - Optional vector search with sentence-transformers
32
27
 
33
28
  ---
34
29
 
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '6.1.0'
60
+ __version__ = '6.2.1'
package/mcp/server.py CHANGED
@@ -1231,10 +1231,15 @@ def _get_chroma_collection():
1231
1231
  """Get or create ChromaDB collection (lazy connection)."""
1232
1232
  global _chroma_client, _chroma_collection
1233
1233
  if _chroma_collection is not None:
1234
- return _chroma_collection
1234
+ try:
1235
+ _chroma_client.heartbeat()
1236
+ return _chroma_collection
1237
+ except Exception:
1238
+ _chroma_client = None
1239
+ _chroma_collection = None
1235
1240
  try:
1236
1241
  import chromadb
1237
- _chroma_client = chromadb.HttpClient(host=CHROMA_HOST, port=int(CHROMA_PORT))
1242
+ _chroma_client = chromadb.HttpClient(host=CHROMA_HOST, port=CHROMA_PORT)
1238
1243
  _chroma_collection = _chroma_client.get_collection(name=CHROMA_COLLECTION)
1239
1244
  return _chroma_collection
1240
1245
  except Exception as e:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "6.1.0",
3
+ "version": "6.2.1",
4
4
  "description": "Loki Mode by Autonomi - Multi-agent autonomous startup system for Claude Code, Codex CLI, and Gemini CLI",
5
5
  "keywords": [
6
6
  "agent",
@@ -0,0 +1,21 @@
1
+ # Cluster Templates
2
+
3
+ Pre-built workflow topologies for common development patterns.
4
+
5
+ ## Usage
6
+
7
+ loki cluster list # List available templates
8
+ loki cluster validate <template> # Validate topology
9
+ loki cluster run <template> [args] # Execute workflow
10
+
11
+ ## Template Format
12
+
13
+ Each template is a JSON file defining agents, their pub/sub topics,
14
+ and the workflow topology.
15
+
16
+ ## Available Templates
17
+
18
+ - security-review: Multi-agent security audit pipeline
19
+ - performance-audit: Performance analysis with profiling
20
+ - refactoring: Structured refactoring with test preservation
21
+ - code-review: Multi-reviewer code review process
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "Code Review",
3
+ "description": "Multi-reviewer code review with conflict resolution",
4
+ "version": "1.0.0",
5
+ "topology": "fan-out-fan-in",
6
+ "agents": [
7
+ {
8
+ "id": "reviewer-arch",
9
+ "type": "review-code",
10
+ "role": "Review architecture and design patterns",
11
+ "subscribes": ["task.start"],
12
+ "publishes": ["review.architecture"]
13
+ },
14
+ {
15
+ "id": "reviewer-security",
16
+ "type": "review-security",
17
+ "role": "Review security implications",
18
+ "subscribes": ["task.start"],
19
+ "publishes": ["review.security"]
20
+ },
21
+ {
22
+ "id": "reviewer-tests",
23
+ "type": "eng-qa",
24
+ "role": "Review test coverage and quality",
25
+ "subscribes": ["task.start"],
26
+ "publishes": ["review.tests"]
27
+ },
28
+ {
29
+ "id": "resolver",
30
+ "type": "review-code",
31
+ "role": "Synthesize all review findings and resolve conflicts",
32
+ "subscribes": ["review.architecture", "review.security", "review.tests"],
33
+ "publishes": ["task.complete"]
34
+ }
35
+ ]
36
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "Performance Audit",
3
+ "description": "Performance analysis with profiling and optimization recommendations",
4
+ "version": "1.0.0",
5
+ "topology": "fan-out-fan-in",
6
+ "agents": [
7
+ {
8
+ "id": "profiler",
9
+ "type": "eng-perf",
10
+ "role": "Profile application and identify bottlenecks",
11
+ "subscribes": ["task.start"],
12
+ "publishes": ["profile.results"]
13
+ },
14
+ {
15
+ "id": "db-analyzer",
16
+ "type": "eng-database",
17
+ "role": "Analyze database queries and index usage",
18
+ "subscribes": ["task.start"],
19
+ "publishes": ["db.analysis"]
20
+ },
21
+ {
22
+ "id": "optimizer",
23
+ "type": "eng-perf",
24
+ "role": "Generate optimization recommendations from all analyses",
25
+ "subscribes": ["profile.results", "db.analysis"],
26
+ "publishes": ["task.complete"]
27
+ }
28
+ ]
29
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "Refactoring",
3
+ "description": "Structured refactoring with test preservation and review",
4
+ "version": "1.0.0",
5
+ "topology": "pipeline",
6
+ "agents": [
7
+ {
8
+ "id": "analyzer",
9
+ "type": "review-code",
10
+ "role": "Analyze code for refactoring opportunities",
11
+ "subscribes": ["task.start"],
12
+ "publishes": ["analysis.complete"]
13
+ },
14
+ {
15
+ "id": "refactorer",
16
+ "type": "eng-backend",
17
+ "role": "Apply refactoring changes with test preservation",
18
+ "subscribes": ["analysis.complete"],
19
+ "publishes": ["refactor.complete"]
20
+ },
21
+ {
22
+ "id": "verifier",
23
+ "type": "eng-qa",
24
+ "role": "Verify all tests pass and behavior is preserved",
25
+ "subscribes": ["refactor.complete"],
26
+ "publishes": ["task.complete"]
27
+ }
28
+ ]
29
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "Security Review",
3
+ "description": "Multi-agent security audit with adversarial testing",
4
+ "version": "1.0.0",
5
+ "topology": "pipeline",
6
+ "agents": [
7
+ {
8
+ "id": "scanner",
9
+ "type": "ops-security",
10
+ "role": "Run SAST/dependency scanning on codebase",
11
+ "subscribes": ["task.start"],
12
+ "publishes": ["scan.results"]
13
+ },
14
+ {
15
+ "id": "reviewer",
16
+ "type": "review-security",
17
+ "role": "Review findings from scanner, assess severity",
18
+ "subscribes": ["scan.results"],
19
+ "publishes": ["review.findings"]
20
+ },
21
+ {
22
+ "id": "adversarial",
23
+ "type": "review-security",
24
+ "role": "Attempt to exploit discovered vulnerabilities",
25
+ "subscribes": ["review.findings"],
26
+ "publishes": ["exploit.results"]
27
+ },
28
+ {
29
+ "id": "reporter",
30
+ "type": "prod-techwriter",
31
+ "role": "Compile security report from all findings",
32
+ "subscribes": ["scan.results", "review.findings", "exploit.results"],
33
+ "publishes": ["task.complete"]
34
+ }
35
+ ]
36
+ }