loki-mode 5.28.1 → 5.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -1
- package/SKILL.md +11 -4
- package/VERSION +1 -1
- package/autonomy/loki +285 -44
- package/autonomy/run.sh +263 -0
- package/autonomy/sandbox.sh +296 -21
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +8 -8
- package/package.json +1 -1
- package/skills/00-index.md +8 -0
- package/skills/agents.md +28 -15
- package/skills/compound-learning.md +274 -0
- package/skills/quality-gates.md +83 -8
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/loki-mode)
|
|
7
7
|
[](https://github.com/asklokesh/loki-mode)
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://github.com/marketplace/actions/loki-mode-code-review)
|
|
9
10
|
[](https://claude.ai)
|
|
10
11
|
[]()
|
|
11
12
|
[](benchmarks/results/)
|
|
@@ -55,7 +56,45 @@ claude --dangerously-skip-permissions
|
|
|
55
56
|
# Then say: Loki Mode with PRD at ./my-prd.md
|
|
56
57
|
```
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
### Option 3: GitHub Action
|
|
60
|
+
|
|
61
|
+
Add automated AI code review to your pull requests:
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
# .github/workflows/loki-review.yml
|
|
65
|
+
name: Loki Code Review
|
|
66
|
+
|
|
67
|
+
on:
|
|
68
|
+
pull_request:
|
|
69
|
+
types: [opened, synchronize]
|
|
70
|
+
|
|
71
|
+
permissions:
|
|
72
|
+
contents: read
|
|
73
|
+
pull-requests: write
|
|
74
|
+
|
|
75
|
+
jobs:
|
|
76
|
+
review:
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
steps:
|
|
79
|
+
- uses: actions/checkout@v4
|
|
80
|
+
- uses: asklokesh/loki-mode@v5
|
|
81
|
+
with:
|
|
82
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
83
|
+
mode: review # review, fix, or test
|
|
84
|
+
provider: claude # claude, codex, or gemini
|
|
85
|
+
max_iterations: 3 # higher = more thorough
|
|
86
|
+
budget_limit: '5.00' # max cost in USD
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Modes:**
|
|
90
|
+
|
|
91
|
+
| Mode | Description |
|
|
92
|
+
|------|-------------|
|
|
93
|
+
| `review` | Analyze PR diff, post structured review as PR comment |
|
|
94
|
+
| `fix` | Automatically fix issues found in the codebase |
|
|
95
|
+
| `test` | Run autonomous test generation and validation |
|
|
96
|
+
|
|
97
|
+
Also available via **Homebrew**, **Docker**, **VS Code Extension**, and **direct shell script**. See the [Installation Guide](docs/INSTALLATION.md) for all 7 installation methods and detailed instructions.
|
|
59
98
|
|
|
60
99
|
### Multi-Provider Support (v5.0.0)
|
|
61
100
|
|
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 zero human intervention. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v5.
|
|
6
|
+
# Loki Mode v5.30.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -62,7 +62,11 @@ REFLECT: Did it work? Update CONTINUITY.md with outcome.
|
|
|
62
62
|
v
|
|
63
63
|
VERIFY: Run tests. Check build. Validate against spec.
|
|
64
64
|
|
|
|
65
|
-
+--[PASS]-->
|
|
65
|
+
+--[PASS]--> COMPOUND: If task had novel insight (bug fix, non-obvious solution,
|
|
66
|
+
| reusable pattern), extract to ~/.loki/solutions/{category}/{slug}.md
|
|
67
|
+
| with YAML frontmatter (title, tags, symptoms, root_cause, prevention).
|
|
68
|
+
| See skills/compound-learning.md for format.
|
|
69
|
+
| Then mark task complete. Return to REASON.
|
|
66
70
|
|
|
|
67
71
|
+--[FAIL]--> Capture error in "Mistakes & Learnings".
|
|
68
72
|
Rollback if needed. Retry with new approach.
|
|
@@ -119,7 +123,8 @@ These rules are ABSOLUTE. Violating them is a critical failure.
|
|
|
119
123
|
```
|
|
120
124
|
BOOTSTRAP ──[project initialized]──> DISCOVERY
|
|
121
125
|
DISCOVERY ──[PRD analyzed, requirements clear]──> ARCHITECTURE
|
|
122
|
-
ARCHITECTURE ──[design approved, specs written]──>
|
|
126
|
+
ARCHITECTURE ──[design approved, specs written]──> DEEPEN_PLAN (standard/complex only)
|
|
127
|
+
DEEPEN_PLAN ──[plan enhanced by 4 research agents]──> INFRASTRUCTURE
|
|
123
128
|
INFRASTRUCTURE ──[cloud/DB ready]──> DEVELOPMENT
|
|
124
129
|
DEVELOPMENT ──[features complete, unit tests pass]──> QA
|
|
125
130
|
QA ──[all tests pass, security clean]──> DEPLOYMENT
|
|
@@ -177,6 +182,8 @@ GROWTH ──[continuous improvement loop]──> GROWTH
|
|
|
177
182
|
- Debugging? Load troubleshooting.md
|
|
178
183
|
- Deploying? Load production.md
|
|
179
184
|
- Parallel features? Load parallel-workflows.md
|
|
185
|
+
- Architecture planning? Load compound-learning.md (deepen-plan)
|
|
186
|
+
- Post-verification? Load compound-learning.md (knowledge extraction)
|
|
180
187
|
3. Read the selected module(s)
|
|
181
188
|
4. Execute with that context
|
|
182
189
|
5. When task category changes: Load new modules (old context discarded)
|
|
@@ -260,4 +267,4 @@ Auto-detected or force with `LOKI_COMPLEXITY`:
|
|
|
260
267
|
|
|
261
268
|
---
|
|
262
269
|
|
|
263
|
-
**v5.
|
|
270
|
+
**v5.30.0 | Compound learning, specialist reviewers, deepen-plan | ~280 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.
|
|
1
|
+
5.30.0
|
package/autonomy/loki
CHANGED
|
@@ -318,6 +318,7 @@ show_help() {
|
|
|
318
318
|
echo " import Import GitHub issues as tasks"
|
|
319
319
|
echo " config [cmd] Manage configuration (show|init|edit|path)"
|
|
320
320
|
echo " memory [cmd] Cross-project learnings (list|show|search|stats)"
|
|
321
|
+
echo " compound [cmd] Knowledge compounding (list|show|search|run|stats)"
|
|
321
322
|
echo " council [cmd] Completion council (status|verdicts|convergence|force-review|report)"
|
|
322
323
|
echo " dogfood Show self-development statistics"
|
|
323
324
|
echo " reset [target] Reset session state (all|retries|failed)"
|
|
@@ -506,33 +507,14 @@ cmd_start() {
|
|
|
506
507
|
local effective_provider="${provider:-${LOKI_PROVIDER:-claude}}"
|
|
507
508
|
|
|
508
509
|
# Handle sandbox mode - delegate to sandbox.sh
|
|
509
|
-
# Skip if we're already inside a
|
|
510
|
-
if [[ "${LOKI_SANDBOX_MODE:-}" == "true" ]] && [[ ! -f /.dockerenv ]]; then
|
|
510
|
+
# Skip if we're already inside a sandbox (IS_SANDBOX=1 or /.dockerenv exists)
|
|
511
|
+
if [[ "${LOKI_SANDBOX_MODE:-}" == "true" ]] && [[ "${IS_SANDBOX:-}" != "1" ]] && [[ ! -f /.dockerenv ]]; then
|
|
511
512
|
if [ ! -f "$SANDBOX_SH" ]; then
|
|
512
513
|
echo -e "${RED}Error: sandbox.sh not found at $SANDBOX_SH${NC}"
|
|
513
514
|
exit 1
|
|
514
515
|
fi
|
|
515
516
|
|
|
516
|
-
|
|
517
|
-
if ! command -v docker &> /dev/null; then
|
|
518
|
-
echo -e "${RED}Error: Docker not found${NC}"
|
|
519
|
-
echo ""
|
|
520
|
-
echo "Docker is required for sandbox mode. Install it:"
|
|
521
|
-
echo " macOS: brew install --cask docker"
|
|
522
|
-
echo " Linux: curl -fsSL https://get.docker.com | sh"
|
|
523
|
-
exit 1
|
|
524
|
-
fi
|
|
525
|
-
|
|
526
|
-
if ! docker info &> /dev/null 2>&1; then
|
|
527
|
-
echo -e "${RED}Error: Docker daemon not running${NC}"
|
|
528
|
-
echo ""
|
|
529
|
-
echo "Start Docker:"
|
|
530
|
-
echo " macOS: Open Docker Desktop app"
|
|
531
|
-
echo " Linux: sudo systemctl start docker"
|
|
532
|
-
exit 1
|
|
533
|
-
fi
|
|
534
|
-
|
|
535
|
-
echo -e "${GREEN}Starting Loki Mode in Docker sandbox...${NC}"
|
|
517
|
+
echo -e "${GREEN}Starting Loki Mode in sandbox...${NC}"
|
|
536
518
|
# Load memory context before sandbox start (errors don't block startup)
|
|
537
519
|
load_memory_context "$prd_file" || true
|
|
538
520
|
emit_event session cli start "provider=$effective_provider" "sandbox=true" "prd_path=${prd_file:-}"
|
|
@@ -2795,6 +2777,7 @@ broadcast_notification() {
|
|
|
2795
2777
|
}
|
|
2796
2778
|
|
|
2797
2779
|
# Sandbox management (delegates to sandbox.sh)
|
|
2780
|
+
# sandbox.sh handles detection: Docker Desktop Sandbox > Docker Container > Worktree
|
|
2798
2781
|
cmd_sandbox() {
|
|
2799
2782
|
local subcommand="${1:-help}"
|
|
2800
2783
|
shift || true
|
|
@@ -2805,28 +2788,7 @@ cmd_sandbox() {
|
|
|
2805
2788
|
exit 1
|
|
2806
2789
|
fi
|
|
2807
2790
|
|
|
2808
|
-
#
|
|
2809
|
-
if ! command -v docker &> /dev/null; then
|
|
2810
|
-
echo -e "${RED}Error: Docker not found${NC}"
|
|
2811
|
-
echo ""
|
|
2812
|
-
echo "Docker is required for sandbox mode. Install it:"
|
|
2813
|
-
echo " macOS: brew install --cask docker"
|
|
2814
|
-
echo " Linux: curl -fsSL https://get.docker.com | sh"
|
|
2815
|
-
echo ""
|
|
2816
|
-
echo "After installation, start Docker Desktop or the Docker daemon."
|
|
2817
|
-
exit 1
|
|
2818
|
-
fi
|
|
2819
|
-
|
|
2820
|
-
if ! docker info &> /dev/null 2>&1; then
|
|
2821
|
-
echo -e "${RED}Error: Docker daemon not running${NC}"
|
|
2822
|
-
echo ""
|
|
2823
|
-
echo "Start Docker:"
|
|
2824
|
-
echo " macOS: Open Docker Desktop app"
|
|
2825
|
-
echo " Linux: sudo systemctl start docker"
|
|
2826
|
-
exit 1
|
|
2827
|
-
fi
|
|
2828
|
-
|
|
2829
|
-
# Delegate to sandbox.sh
|
|
2791
|
+
# Delegate to sandbox.sh (it handles Docker/worktree detection internally)
|
|
2830
2792
|
exec "$SANDBOX_SH" "$subcommand" "$@"
|
|
2831
2793
|
}
|
|
2832
2794
|
|
|
@@ -3585,6 +3547,9 @@ main() {
|
|
|
3585
3547
|
memory)
|
|
3586
3548
|
cmd_memory "$@"
|
|
3587
3549
|
;;
|
|
3550
|
+
compound)
|
|
3551
|
+
cmd_compound "$@"
|
|
3552
|
+
;;
|
|
3588
3553
|
council)
|
|
3589
3554
|
cmd_council "$@"
|
|
3590
3555
|
;;
|
|
@@ -4580,6 +4545,282 @@ except Exception as e:
|
|
|
4580
4545
|
esac
|
|
4581
4546
|
}
|
|
4582
4547
|
|
|
4548
|
+
# Knowledge Compounding - Structured Solutions (v5.30.0)
|
|
4549
|
+
# Inspired by Compound Engineering Plugin's docs/solutions/ with YAML frontmatter
|
|
4550
|
+
cmd_compound() {
|
|
4551
|
+
local subcommand="${1:-help}"
|
|
4552
|
+
shift 2>/dev/null || true
|
|
4553
|
+
|
|
4554
|
+
local solutions_dir="${HOME}/.loki/solutions"
|
|
4555
|
+
|
|
4556
|
+
case "$subcommand" in
|
|
4557
|
+
list|ls)
|
|
4558
|
+
echo -e "${BOLD}Compound Solutions${NC}"
|
|
4559
|
+
echo ""
|
|
4560
|
+
|
|
4561
|
+
if [ ! -d "$solutions_dir" ]; then
|
|
4562
|
+
echo " No solutions yet. Solutions are created automatically during"
|
|
4563
|
+
echo " Loki sessions or manually with 'loki compound run'."
|
|
4564
|
+
echo ""
|
|
4565
|
+
echo " Location: $solutions_dir"
|
|
4566
|
+
return
|
|
4567
|
+
fi
|
|
4568
|
+
|
|
4569
|
+
local total=0
|
|
4570
|
+
for category in security performance architecture testing debugging deployment general; do
|
|
4571
|
+
local cat_dir="$solutions_dir/$category"
|
|
4572
|
+
if [ -d "$cat_dir" ]; then
|
|
4573
|
+
local count=$(find "$cat_dir" -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
4574
|
+
if [ "$count" -gt 0 ]; then
|
|
4575
|
+
printf " %-16s ${GREEN}%d${NC} solutions\n" "$category" "$count"
|
|
4576
|
+
total=$((total + count))
|
|
4577
|
+
fi
|
|
4578
|
+
fi
|
|
4579
|
+
done
|
|
4580
|
+
|
|
4581
|
+
if [ "$total" -eq 0 ]; then
|
|
4582
|
+
echo " No solutions yet."
|
|
4583
|
+
else
|
|
4584
|
+
echo ""
|
|
4585
|
+
echo " Total: ${total} solutions"
|
|
4586
|
+
fi
|
|
4587
|
+
echo ""
|
|
4588
|
+
echo " Location: $solutions_dir"
|
|
4589
|
+
echo ""
|
|
4590
|
+
echo " Use 'loki compound show <category>' to view solutions"
|
|
4591
|
+
;;
|
|
4592
|
+
|
|
4593
|
+
show)
|
|
4594
|
+
local category="${1:-}"
|
|
4595
|
+
if [ -z "$category" ]; then
|
|
4596
|
+
echo -e "${RED}Error: Specify a category${NC}"
|
|
4597
|
+
echo "Categories: security, performance, architecture, testing, debugging, deployment, general"
|
|
4598
|
+
return 1
|
|
4599
|
+
fi
|
|
4600
|
+
|
|
4601
|
+
local cat_dir="$solutions_dir/$category"
|
|
4602
|
+
if [ ! -d "$cat_dir" ]; then
|
|
4603
|
+
echo "No solutions in category: $category"
|
|
4604
|
+
return
|
|
4605
|
+
fi
|
|
4606
|
+
|
|
4607
|
+
echo -e "${BOLD}Solutions: $category${NC}"
|
|
4608
|
+
echo ""
|
|
4609
|
+
|
|
4610
|
+
for file in "$cat_dir"/*.md; do
|
|
4611
|
+
[ -f "$file" ] || continue
|
|
4612
|
+
# Extract title from YAML frontmatter
|
|
4613
|
+
local title=$(grep '^title:' "$file" 2>/dev/null | head -1 | sed 's/title: *"//;s/"$//')
|
|
4614
|
+
local confidence=$(grep '^confidence:' "$file" 2>/dev/null | head -1 | sed 's/confidence: *//')
|
|
4615
|
+
local project=$(grep '^source_project:' "$file" 2>/dev/null | head -1 | sed 's/source_project: *"//;s/"$//')
|
|
4616
|
+
echo -e " ${GREEN}*${NC} ${title:-$(basename "$file" .md)}"
|
|
4617
|
+
[ -n "$confidence" ] && echo -e " confidence: ${confidence} project: ${project:-unknown}"
|
|
4618
|
+
done
|
|
4619
|
+
echo ""
|
|
4620
|
+
;;
|
|
4621
|
+
|
|
4622
|
+
search)
|
|
4623
|
+
local query="${1:-}"
|
|
4624
|
+
if [ -z "$query" ]; then
|
|
4625
|
+
echo -e "${RED}Error: Specify a search query${NC}"
|
|
4626
|
+
echo "Usage: loki compound search <query>"
|
|
4627
|
+
return 1
|
|
4628
|
+
fi
|
|
4629
|
+
|
|
4630
|
+
echo -e "${BOLD}Searching solutions for: ${query}${NC}"
|
|
4631
|
+
echo ""
|
|
4632
|
+
|
|
4633
|
+
if [ ! -d "$solutions_dir" ]; then
|
|
4634
|
+
echo "No solutions directory found."
|
|
4635
|
+
return
|
|
4636
|
+
fi
|
|
4637
|
+
|
|
4638
|
+
local found=0
|
|
4639
|
+
while IFS= read -r file; do
|
|
4640
|
+
local title=$(grep '^title:' "$file" 2>/dev/null | head -1 | sed 's/title: *"//;s/"$//')
|
|
4641
|
+
local category=$(grep '^category:' "$file" 2>/dev/null | head -1 | sed 's/category: *//')
|
|
4642
|
+
echo -e " [${CYAN}${category}${NC}] ${title:-$(basename "$file" .md)}"
|
|
4643
|
+
echo -e " ${DIM}${file}${NC}"
|
|
4644
|
+
found=$((found + 1))
|
|
4645
|
+
done < <(grep -rl "$query" "$solutions_dir" 2>/dev/null || true)
|
|
4646
|
+
|
|
4647
|
+
if [ "$found" -eq 0 ]; then
|
|
4648
|
+
echo " No solutions matching: $query"
|
|
4649
|
+
else
|
|
4650
|
+
echo ""
|
|
4651
|
+
echo " Found: ${found} solutions"
|
|
4652
|
+
fi
|
|
4653
|
+
;;
|
|
4654
|
+
|
|
4655
|
+
run)
|
|
4656
|
+
echo -e "${BOLD}Compounding learnings into solutions...${NC}"
|
|
4657
|
+
echo ""
|
|
4658
|
+
|
|
4659
|
+
local learnings_dir="${HOME}/.loki/learnings"
|
|
4660
|
+
if [ ! -d "$learnings_dir" ]; then
|
|
4661
|
+
echo " No learnings directory found at: $learnings_dir"
|
|
4662
|
+
echo " Run a Loki session first to generate learnings."
|
|
4663
|
+
return
|
|
4664
|
+
fi
|
|
4665
|
+
|
|
4666
|
+
python3 << 'COMPOUND_RUN_SCRIPT'
|
|
4667
|
+
import json, os, re
|
|
4668
|
+
from datetime import datetime, timezone
|
|
4669
|
+
from collections import defaultdict
|
|
4670
|
+
learnings_dir = os.path.expanduser("~/.loki/learnings")
|
|
4671
|
+
solutions_dir = os.path.expanduser("~/.loki/solutions")
|
|
4672
|
+
CATEGORIES = ["security", "performance", "architecture", "testing", "debugging", "deployment", "general"]
|
|
4673
|
+
CATEGORY_KEYWORDS = {
|
|
4674
|
+
"security": ["auth", "login", "password", "token", "injection", "xss", "csrf", "cors", "secret", "encrypt", "permission"],
|
|
4675
|
+
"performance": ["cache", "query", "n+1", "memory", "leak", "slow", "timeout", "pool", "index", "optimize", "bundle"],
|
|
4676
|
+
"architecture": ["pattern", "solid", "coupling", "abstraction", "module", "interface", "design", "refactor", "structure"],
|
|
4677
|
+
"testing": ["test", "mock", "fixture", "coverage", "assert", "spec", "e2e", "playwright", "jest", "flaky"],
|
|
4678
|
+
"debugging": ["debug", "error", "trace", "log", "stack", "crash", "exception", "breakpoint", "inspect"],
|
|
4679
|
+
"deployment": ["deploy", "docker", "ci", "cd", "pipeline", "kubernetes", "nginx", "ssl", "domain", "env", "config"],
|
|
4680
|
+
}
|
|
4681
|
+
def load_jsonl(filepath):
|
|
4682
|
+
entries = []
|
|
4683
|
+
if not os.path.exists(filepath): return entries
|
|
4684
|
+
with open(filepath, 'r') as f:
|
|
4685
|
+
for line in f:
|
|
4686
|
+
try:
|
|
4687
|
+
entry = json.loads(line)
|
|
4688
|
+
if 'description' in entry: entries.append(entry)
|
|
4689
|
+
except: continue
|
|
4690
|
+
return entries
|
|
4691
|
+
def classify_category(description):
|
|
4692
|
+
desc_lower = description.lower()
|
|
4693
|
+
scores = {cat: sum(1 for kw in kws if kw in desc_lower) for cat, kws in CATEGORY_KEYWORDS.items()}
|
|
4694
|
+
best = max(scores, key=scores.get)
|
|
4695
|
+
return best if scores[best] > 0 else "general"
|
|
4696
|
+
def slugify(text):
|
|
4697
|
+
return re.sub(r'[^a-z0-9]+', '-', text.lower().strip()).strip('-')[:80]
|
|
4698
|
+
def solution_exists(solutions_dir, title_slug):
|
|
4699
|
+
for cat in CATEGORIES:
|
|
4700
|
+
cat_dir = os.path.join(solutions_dir, cat)
|
|
4701
|
+
if os.path.exists(cat_dir) and os.path.exists(os.path.join(cat_dir, f"{title_slug}.md")):
|
|
4702
|
+
return True
|
|
4703
|
+
return False
|
|
4704
|
+
patterns = load_jsonl(os.path.join(learnings_dir, "patterns.jsonl"))
|
|
4705
|
+
mistakes = load_jsonl(os.path.join(learnings_dir, "mistakes.jsonl"))
|
|
4706
|
+
successes = load_jsonl(os.path.join(learnings_dir, "successes.jsonl"))
|
|
4707
|
+
print(f" Loaded: {len(patterns)} patterns, {len(mistakes)} mistakes, {len(successes)} successes")
|
|
4708
|
+
grouped = defaultdict(list)
|
|
4709
|
+
for entry in patterns + mistakes + successes:
|
|
4710
|
+
grouped[classify_category(entry.get('description', ''))].append(entry)
|
|
4711
|
+
created = 0
|
|
4712
|
+
now = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
4713
|
+
for category, entries in grouped.items():
|
|
4714
|
+
if len(entries) < 2: continue
|
|
4715
|
+
cat_dir = os.path.join(solutions_dir, category)
|
|
4716
|
+
os.makedirs(cat_dir, exist_ok=True)
|
|
4717
|
+
best_entry = max(entries, key=lambda e: len(e.get('description', '')))
|
|
4718
|
+
title = best_entry['description'][:120]
|
|
4719
|
+
slug = slugify(title)
|
|
4720
|
+
if solution_exists(solutions_dir, slug): continue
|
|
4721
|
+
all_words = ' '.join(e.get('description', '') for e in entries).lower()
|
|
4722
|
+
tags = []
|
|
4723
|
+
for kw_list in CATEGORY_KEYWORDS.values():
|
|
4724
|
+
for kw in kw_list:
|
|
4725
|
+
if kw in all_words and kw not in tags: tags.append(kw)
|
|
4726
|
+
tags = tags[:8]
|
|
4727
|
+
symptoms = [e.get('description', '')[:200] for e in entries
|
|
4728
|
+
if any(w in e.get('description', '').lower() for w in ['error', 'fail', 'bug', 'crash', 'issue'])][:4]
|
|
4729
|
+
if not symptoms: symptoms = [entries[0].get('description', '')[:200]]
|
|
4730
|
+
solution_lines = [f"- {e.get('description', '')}" for e in entries
|
|
4731
|
+
if not any(w in e.get('description', '').lower() for w in ['error', 'fail', 'bug', 'crash'])]
|
|
4732
|
+
if not solution_lines: solution_lines = [f"- {entries[0].get('description', '')}"]
|
|
4733
|
+
project = best_entry.get('project', os.path.basename(os.getcwd()))
|
|
4734
|
+
filepath = os.path.join(cat_dir, f"{slug}.md")
|
|
4735
|
+
with open(filepath, 'w') as f:
|
|
4736
|
+
f.write(f"---\ntitle: \"{title}\"\ncategory: {category}\ntags: [{', '.join(tags)}]\nsymptoms:\n")
|
|
4737
|
+
for s in symptoms: f.write(f' - "{s}"\n')
|
|
4738
|
+
f.write(f'root_cause: "Identified from {len(entries)} related learnings"\n')
|
|
4739
|
+
f.write(f'prevention: "See solution details below"\nconfidence: {min(0.5 + 0.1 * len(entries), 0.95):.2f}\n')
|
|
4740
|
+
f.write(f'source_project: "{project}"\ncreated: "{now}"\napplied_count: 0\n---\n\n')
|
|
4741
|
+
f.write("## Solution\n\n" + '\n'.join(solution_lines) + '\n\n')
|
|
4742
|
+
f.write(f"## Context\n\nCompounded from {len(entries)} learnings from project: {project}\n")
|
|
4743
|
+
created += 1
|
|
4744
|
+
print(f" Created: {category}/{slug}.md")
|
|
4745
|
+
if created > 0: print(f"\n Compounded {created} new solution files to {solutions_dir}")
|
|
4746
|
+
else: print(" No new solutions to compound (need 2+ related learnings per category)")
|
|
4747
|
+
COMPOUND_RUN_SCRIPT
|
|
4748
|
+
;;
|
|
4749
|
+
|
|
4750
|
+
stats)
|
|
4751
|
+
echo -e "${BOLD}Compound Solution Statistics${NC}"
|
|
4752
|
+
echo ""
|
|
4753
|
+
|
|
4754
|
+
if [ ! -d "$solutions_dir" ]; then
|
|
4755
|
+
echo " No solutions directory found."
|
|
4756
|
+
return
|
|
4757
|
+
fi
|
|
4758
|
+
|
|
4759
|
+
local total=0
|
|
4760
|
+
local newest=""
|
|
4761
|
+
local oldest=""
|
|
4762
|
+
|
|
4763
|
+
for category in security performance architecture testing debugging deployment general; do
|
|
4764
|
+
local cat_dir="$solutions_dir/$category"
|
|
4765
|
+
if [ -d "$cat_dir" ]; then
|
|
4766
|
+
local count=$(find "$cat_dir" -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
4767
|
+
total=$((total + count))
|
|
4768
|
+
fi
|
|
4769
|
+
done
|
|
4770
|
+
|
|
4771
|
+
echo " Total solutions: ${total}"
|
|
4772
|
+
|
|
4773
|
+
if [ "$total" -gt 0 ]; then
|
|
4774
|
+
# Find newest and oldest
|
|
4775
|
+
newest=$(find "$solutions_dir" -name "*.md" -exec stat -f '%m %N' {} \; 2>/dev/null | sort -rn | head -1 | cut -d' ' -f2-)
|
|
4776
|
+
oldest=$(find "$solutions_dir" -name "*.md" -exec stat -f '%m %N' {} \; 2>/dev/null | sort -n | head -1 | cut -d' ' -f2-)
|
|
4777
|
+
|
|
4778
|
+
if [ -n "$newest" ]; then
|
|
4779
|
+
local newest_title=$(grep '^title:' "$newest" 2>/dev/null | head -1 | sed 's/title: *"//;s/"$//')
|
|
4780
|
+
echo " Newest: ${newest_title:-$(basename "$newest" .md)}"
|
|
4781
|
+
fi
|
|
4782
|
+
if [ -n "$oldest" ]; then
|
|
4783
|
+
local oldest_title=$(grep '^title:' "$oldest" 2>/dev/null | head -1 | sed 's/title: *"//;s/"$//')
|
|
4784
|
+
echo " Oldest: ${oldest_title:-$(basename "$oldest" .md)}"
|
|
4785
|
+
fi
|
|
4786
|
+
fi
|
|
4787
|
+
|
|
4788
|
+
echo ""
|
|
4789
|
+
echo " Location: $solutions_dir"
|
|
4790
|
+
;;
|
|
4791
|
+
|
|
4792
|
+
help|--help|-h)
|
|
4793
|
+
echo -e "${BOLD}loki compound${NC} - Knowledge compounding system"
|
|
4794
|
+
echo ""
|
|
4795
|
+
echo "Extracts structured solutions from cross-project learnings."
|
|
4796
|
+
echo "Solutions are stored as markdown files with YAML frontmatter"
|
|
4797
|
+
echo "at ~/.loki/solutions/{category}/ and fed back into future planning."
|
|
4798
|
+
echo ""
|
|
4799
|
+
echo "Usage: loki compound <command>"
|
|
4800
|
+
echo ""
|
|
4801
|
+
echo "Commands:"
|
|
4802
|
+
echo " list List all solutions by category"
|
|
4803
|
+
echo " show <category> Show solutions in a category"
|
|
4804
|
+
echo " search <query> Search across all solutions"
|
|
4805
|
+
echo " run Manually compound current learnings into solutions"
|
|
4806
|
+
echo " stats Solution statistics"
|
|
4807
|
+
echo " help Show this help"
|
|
4808
|
+
echo ""
|
|
4809
|
+
echo "Categories: security, performance, architecture, testing,"
|
|
4810
|
+
echo " debugging, deployment, general"
|
|
4811
|
+
echo ""
|
|
4812
|
+
echo "Solutions are created automatically at session end and"
|
|
4813
|
+
echo "loaded during the REASON phase for relevant tasks."
|
|
4814
|
+
;;
|
|
4815
|
+
|
|
4816
|
+
*)
|
|
4817
|
+
echo -e "${RED}Unknown compound command: $subcommand${NC}"
|
|
4818
|
+
echo "Run 'loki compound help' for usage."
|
|
4819
|
+
return 1
|
|
4820
|
+
;;
|
|
4821
|
+
esac
|
|
4822
|
+
}
|
|
4823
|
+
|
|
4583
4824
|
# Completion Council management
|
|
4584
4825
|
cmd_council() {
|
|
4585
4826
|
local subcommand="${1:-status}"
|