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 +1 -1
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/bmad-adapter.py +3 -2
- package/autonomy/loki +341 -1
- package/autonomy/prd-analyzer.py +3 -3
- package/autonomy/run.sh +20 -4
- package/autonomy/sandbox.sh +189 -1
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +19 -24
- package/mcp/__init__.py +1 -1
- package/mcp/server.py +7 -2
- package/package.json +1 -1
- package/templates/clusters/README.md +21 -0
- package/templates/clusters/code-review.json +36 -0
- package/templates/clusters/performance-audit.json +29 -0
- package/templates/clusters/refactoring.json +29 -0
- package/templates/clusters/security-review.json +36 -0
package/README.md
CHANGED
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
|
|
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
|
|
266
|
+
**v6.2.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.1
|
|
1
|
+
6.2.1
|
package/autonomy/bmad-adapter.py
CHANGED
|
@@ -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
|
|
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(
|
|
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: $
|
|
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
|
;;
|
package/autonomy/prd-analyzer.py
CHANGED
|
@@ -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
|
|
263
|
-
|
|
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=$(
|
|
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'
|
|
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":
|
|
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"),
|
package/autonomy/sandbox.sh
CHANGED
|
@@ -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
|
-
|
|
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")
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
|
@@ -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
|
|
5
|
+
**Version:** v6.2.1
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
## What's New in
|
|
10
|
-
|
|
11
|
-
###
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
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
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
|
-
|
|
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=
|
|
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
|
@@ -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
|
+
}
|