loki-mode 5.29.0 → 5.31.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 +38 -2
- package/SKILL.md +11 -4
- package/VERSION +1 -1
- package/autonomy/loki +374 -12
- package/autonomy/run.sh +279 -3
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +110 -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
|
@@ -82,8 +82,44 @@ jobs:
|
|
|
82
82
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
83
83
|
mode: review # review, fix, or test
|
|
84
84
|
provider: claude # claude, codex, or gemini
|
|
85
|
-
max_iterations: 3 #
|
|
86
|
-
budget_limit: '5.00' # max cost in USD
|
|
85
|
+
max_iterations: 3 # sets LOKI_MAX_ITERATIONS env var
|
|
86
|
+
budget_limit: '5.00' # max cost in USD (maps to --budget flag)
|
|
87
|
+
env:
|
|
88
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Prerequisites:**
|
|
92
|
+
- An API key for your chosen provider (set as a repository secret):
|
|
93
|
+
- Claude: `ANTHROPIC_API_KEY`
|
|
94
|
+
- Codex: `OPENAI_API_KEY`
|
|
95
|
+
- Gemini: `GOOGLE_API_KEY`
|
|
96
|
+
- The action automatically installs `loki-mode` and `@anthropic-ai/claude-code` (for the Claude provider)
|
|
97
|
+
|
|
98
|
+
**Action Inputs:**
|
|
99
|
+
|
|
100
|
+
| Input | Default | Description |
|
|
101
|
+
|-------|---------|-------------|
|
|
102
|
+
| `mode` | `review` | `review`, `fix`, or `test` |
|
|
103
|
+
| `provider` | `claude` | `claude`, `codex`, or `gemini` |
|
|
104
|
+
| `budget_limit` | `5.00` | Max cost in USD (maps to `--budget` CLI flag) |
|
|
105
|
+
| `budget` | | Alias for `budget_limit` |
|
|
106
|
+
| `max_iterations` | `3` | Sets `LOKI_MAX_ITERATIONS` env var |
|
|
107
|
+
| `github_token` | (required) | GitHub token for PR comments |
|
|
108
|
+
| `prd_file` | | Path to PRD file relative to repo root |
|
|
109
|
+
| `auto_confirm` | `true` | Skip confirmation prompts (always true in CI) |
|
|
110
|
+
| `install_claude` | `true` | Auto-install Claude Code CLI if not present |
|
|
111
|
+
| `node_version` | `20` | Node.js version |
|
|
112
|
+
|
|
113
|
+
**Using with a PRD file (fix/test modes):**
|
|
114
|
+
|
|
115
|
+
```yaml
|
|
116
|
+
- uses: asklokesh/loki-mode@v5
|
|
117
|
+
with:
|
|
118
|
+
mode: fix
|
|
119
|
+
prd_file: 'docs/my-prd.md'
|
|
120
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
121
|
+
env:
|
|
122
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
87
123
|
```
|
|
88
124
|
|
|
89
125
|
**Modes:**
|
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.31.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.31.0 | Shell completions, GitHub Action enhancements, auto-confirm | ~280 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.
|
|
1
|
+
5.31.0
|
package/autonomy/loki
CHANGED
|
@@ -317,7 +317,9 @@ show_help() {
|
|
|
317
317
|
echo " voice [cmd] Voice input for PRD creation (status|listen|dictate|speak|start)"
|
|
318
318
|
echo " import Import GitHub issues as tasks"
|
|
319
319
|
echo " config [cmd] Manage configuration (show|init|edit|path)"
|
|
320
|
+
echo " completions [bash|zsh] Output shell completion scripts"
|
|
320
321
|
echo " memory [cmd] Cross-project learnings (list|show|search|stats)"
|
|
322
|
+
echo " compound [cmd] Knowledge compounding (list|show|search|run|stats)"
|
|
321
323
|
echo " council [cmd] Completion council (status|verdicts|convergence|force-review|report)"
|
|
322
324
|
echo " dogfood Show self-development statistics"
|
|
323
325
|
echo " reset [target] Reset session state (all|retries|failed)"
|
|
@@ -388,12 +390,22 @@ cmd_start() {
|
|
|
388
390
|
echo " --sandbox Run in Docker sandbox"
|
|
389
391
|
echo " --skip-memory Skip loading memory context at startup"
|
|
390
392
|
echo " --budget USD Cost budget limit (auto-pause when exceeded)"
|
|
393
|
+
echo " --yes, -y Skip confirmation prompts (auto-confirm)"
|
|
394
|
+
echo ""
|
|
395
|
+
echo "Environment Variables:"
|
|
396
|
+
echo " LOKI_PRD_FILE Path to PRD file (alternative to positional arg)"
|
|
397
|
+
echo " LOKI_AUTO_CONFIRM Set to 'true' to skip confirmation prompts"
|
|
398
|
+
echo " CI When 'true', auto-confirms prompts"
|
|
399
|
+
echo " LOKI_MAX_ITERATIONS Max iteration count"
|
|
400
|
+
echo " LOKI_BUDGET_LIMIT Cost budget limit in USD"
|
|
391
401
|
echo ""
|
|
392
402
|
echo "Examples:"
|
|
393
403
|
echo " loki start # Interactive or resume existing"
|
|
394
404
|
echo " loki start ./prd.md # Start with PRD file"
|
|
395
405
|
echo " loki start ./prd.md --parallel # Parallel mode with worktrees"
|
|
396
406
|
echo " loki start --provider codex # Use OpenAI Codex CLI"
|
|
407
|
+
echo " loki start --yes # Skip confirmation prompt"
|
|
408
|
+
echo " LOKI_PRD_FILE=./prd.md loki start # PRD via env var"
|
|
397
409
|
exit 0
|
|
398
410
|
;;
|
|
399
411
|
--provider)
|
|
@@ -443,6 +455,10 @@ cmd_start() {
|
|
|
443
455
|
export LOKI_SKIP_MEMORY=true
|
|
444
456
|
shift
|
|
445
457
|
;;
|
|
458
|
+
--yes|-y)
|
|
459
|
+
export LOKI_AUTO_CONFIRM=true
|
|
460
|
+
shift
|
|
461
|
+
;;
|
|
446
462
|
--budget)
|
|
447
463
|
if [[ -n "${2:-}" ]]; then
|
|
448
464
|
if ! echo "$2" | grep -qE '^[0-9]+(\.[0-9]+)?$'; then
|
|
@@ -476,19 +492,29 @@ cmd_start() {
|
|
|
476
492
|
esac
|
|
477
493
|
done
|
|
478
494
|
|
|
495
|
+
# Support LOKI_PRD_FILE environment variable as fallback
|
|
496
|
+
if [ -z "$prd_file" ] && [ -n "${LOKI_PRD_FILE:-}" ]; then
|
|
497
|
+
prd_file="$LOKI_PRD_FILE"
|
|
498
|
+
fi
|
|
499
|
+
|
|
479
500
|
if [ -n "$prd_file" ]; then
|
|
480
501
|
args+=("$prd_file")
|
|
481
502
|
else
|
|
482
503
|
# No PRD file specified -- warn and confirm before consuming API credits
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
echo "
|
|
491
|
-
|
|
504
|
+
# Auto-confirm in CI environments or when LOKI_AUTO_CONFIRM is set
|
|
505
|
+
if [[ "${LOKI_AUTO_CONFIRM:-}" == "true" ]] || [[ "${CI:-}" == "true" ]]; then
|
|
506
|
+
echo -e "${YELLOW}Warning: No PRD file specified. Auto-confirming (CI mode).${NC}"
|
|
507
|
+
else
|
|
508
|
+
echo -e "${YELLOW}Warning: No PRD file specified.${NC}"
|
|
509
|
+
echo "Loki Mode will start autonomous execution in the current directory"
|
|
510
|
+
echo "without a requirements document."
|
|
511
|
+
echo ""
|
|
512
|
+
echo -e "This will consume API credits. Continue? [y/N] \c"
|
|
513
|
+
read -r confirm
|
|
514
|
+
if [[ ! "$confirm" =~ ^[Yy] ]]; then
|
|
515
|
+
echo "Aborted. Usage: loki start <path-to-prd.md>"
|
|
516
|
+
exit 0
|
|
517
|
+
fi
|
|
492
518
|
fi
|
|
493
519
|
fi
|
|
494
520
|
|
|
@@ -3546,6 +3572,9 @@ main() {
|
|
|
3546
3572
|
memory)
|
|
3547
3573
|
cmd_memory "$@"
|
|
3548
3574
|
;;
|
|
3575
|
+
compound)
|
|
3576
|
+
cmd_compound "$@"
|
|
3577
|
+
;;
|
|
3549
3578
|
council)
|
|
3550
3579
|
cmd_council "$@"
|
|
3551
3580
|
;;
|
|
@@ -3564,6 +3593,9 @@ main() {
|
|
|
3564
3593
|
version|--version|-v)
|
|
3565
3594
|
cmd_version
|
|
3566
3595
|
;;
|
|
3596
|
+
completions)
|
|
3597
|
+
cmd_completions "$@"
|
|
3598
|
+
;;
|
|
3567
3599
|
help|--help|-h)
|
|
3568
3600
|
show_help
|
|
3569
3601
|
;;
|
|
@@ -3841,9 +3873,13 @@ for filename in ['patterns.jsonl', 'mistakes.jsonl', 'successes.jsonl']:
|
|
|
3841
3873
|
local type="${2:-}"
|
|
3842
3874
|
|
|
3843
3875
|
if [ -z "$type" ]; then
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3876
|
+
if [[ "${LOKI_AUTO_CONFIRM:-}" == "true" ]] || [[ "${CI:-}" == "true" ]]; then
|
|
3877
|
+
confirm="yes"
|
|
3878
|
+
else
|
|
3879
|
+
echo -e "${YELLOW}This will delete ALL cross-project learnings.${NC}"
|
|
3880
|
+
echo -n "Are you sure? (yes/no): "
|
|
3881
|
+
read -r confirm
|
|
3882
|
+
fi
|
|
3847
3883
|
if [ "$confirm" = "yes" ]; then
|
|
3848
3884
|
rm -rf "$learnings_dir"
|
|
3849
3885
|
mkdir -p "$learnings_dir"
|
|
@@ -4541,6 +4577,282 @@ except Exception as e:
|
|
|
4541
4577
|
esac
|
|
4542
4578
|
}
|
|
4543
4579
|
|
|
4580
|
+
# Knowledge Compounding - Structured Solutions (v5.30.0)
|
|
4581
|
+
# Inspired by Compound Engineering Plugin's docs/solutions/ with YAML frontmatter
|
|
4582
|
+
cmd_compound() {
|
|
4583
|
+
local subcommand="${1:-help}"
|
|
4584
|
+
shift 2>/dev/null || true
|
|
4585
|
+
|
|
4586
|
+
local solutions_dir="${HOME}/.loki/solutions"
|
|
4587
|
+
|
|
4588
|
+
case "$subcommand" in
|
|
4589
|
+
list|ls)
|
|
4590
|
+
echo -e "${BOLD}Compound Solutions${NC}"
|
|
4591
|
+
echo ""
|
|
4592
|
+
|
|
4593
|
+
if [ ! -d "$solutions_dir" ]; then
|
|
4594
|
+
echo " No solutions yet. Solutions are created automatically during"
|
|
4595
|
+
echo " Loki sessions or manually with 'loki compound run'."
|
|
4596
|
+
echo ""
|
|
4597
|
+
echo " Location: $solutions_dir"
|
|
4598
|
+
return
|
|
4599
|
+
fi
|
|
4600
|
+
|
|
4601
|
+
local total=0
|
|
4602
|
+
for category in security performance architecture testing debugging deployment general; do
|
|
4603
|
+
local cat_dir="$solutions_dir/$category"
|
|
4604
|
+
if [ -d "$cat_dir" ]; then
|
|
4605
|
+
local count=$(find "$cat_dir" -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
4606
|
+
if [ "$count" -gt 0 ]; then
|
|
4607
|
+
printf " %-16s ${GREEN}%d${NC} solutions\n" "$category" "$count"
|
|
4608
|
+
total=$((total + count))
|
|
4609
|
+
fi
|
|
4610
|
+
fi
|
|
4611
|
+
done
|
|
4612
|
+
|
|
4613
|
+
if [ "$total" -eq 0 ]; then
|
|
4614
|
+
echo " No solutions yet."
|
|
4615
|
+
else
|
|
4616
|
+
echo ""
|
|
4617
|
+
echo " Total: ${total} solutions"
|
|
4618
|
+
fi
|
|
4619
|
+
echo ""
|
|
4620
|
+
echo " Location: $solutions_dir"
|
|
4621
|
+
echo ""
|
|
4622
|
+
echo " Use 'loki compound show <category>' to view solutions"
|
|
4623
|
+
;;
|
|
4624
|
+
|
|
4625
|
+
show)
|
|
4626
|
+
local category="${1:-}"
|
|
4627
|
+
if [ -z "$category" ]; then
|
|
4628
|
+
echo -e "${RED}Error: Specify a category${NC}"
|
|
4629
|
+
echo "Categories: security, performance, architecture, testing, debugging, deployment, general"
|
|
4630
|
+
return 1
|
|
4631
|
+
fi
|
|
4632
|
+
|
|
4633
|
+
local cat_dir="$solutions_dir/$category"
|
|
4634
|
+
if [ ! -d "$cat_dir" ]; then
|
|
4635
|
+
echo "No solutions in category: $category"
|
|
4636
|
+
return
|
|
4637
|
+
fi
|
|
4638
|
+
|
|
4639
|
+
echo -e "${BOLD}Solutions: $category${NC}"
|
|
4640
|
+
echo ""
|
|
4641
|
+
|
|
4642
|
+
for file in "$cat_dir"/*.md; do
|
|
4643
|
+
[ -f "$file" ] || continue
|
|
4644
|
+
# Extract title from YAML frontmatter
|
|
4645
|
+
local title=$(grep '^title:' "$file" 2>/dev/null | head -1 | sed 's/title: *"//;s/"$//')
|
|
4646
|
+
local confidence=$(grep '^confidence:' "$file" 2>/dev/null | head -1 | sed 's/confidence: *//')
|
|
4647
|
+
local project=$(grep '^source_project:' "$file" 2>/dev/null | head -1 | sed 's/source_project: *"//;s/"$//')
|
|
4648
|
+
echo -e " ${GREEN}*${NC} ${title:-$(basename "$file" .md)}"
|
|
4649
|
+
[ -n "$confidence" ] && echo -e " confidence: ${confidence} project: ${project:-unknown}"
|
|
4650
|
+
done
|
|
4651
|
+
echo ""
|
|
4652
|
+
;;
|
|
4653
|
+
|
|
4654
|
+
search)
|
|
4655
|
+
local query="${1:-}"
|
|
4656
|
+
if [ -z "$query" ]; then
|
|
4657
|
+
echo -e "${RED}Error: Specify a search query${NC}"
|
|
4658
|
+
echo "Usage: loki compound search <query>"
|
|
4659
|
+
return 1
|
|
4660
|
+
fi
|
|
4661
|
+
|
|
4662
|
+
echo -e "${BOLD}Searching solutions for: ${query}${NC}"
|
|
4663
|
+
echo ""
|
|
4664
|
+
|
|
4665
|
+
if [ ! -d "$solutions_dir" ]; then
|
|
4666
|
+
echo "No solutions directory found."
|
|
4667
|
+
return
|
|
4668
|
+
fi
|
|
4669
|
+
|
|
4670
|
+
local found=0
|
|
4671
|
+
while IFS= read -r file; do
|
|
4672
|
+
local title=$(grep '^title:' "$file" 2>/dev/null | head -1 | sed 's/title: *"//;s/"$//')
|
|
4673
|
+
local category=$(grep '^category:' "$file" 2>/dev/null | head -1 | sed 's/category: *//')
|
|
4674
|
+
echo -e " [${CYAN}${category}${NC}] ${title:-$(basename "$file" .md)}"
|
|
4675
|
+
echo -e " ${DIM}${file}${NC}"
|
|
4676
|
+
found=$((found + 1))
|
|
4677
|
+
done < <(grep -rl "$query" "$solutions_dir" 2>/dev/null || true)
|
|
4678
|
+
|
|
4679
|
+
if [ "$found" -eq 0 ]; then
|
|
4680
|
+
echo " No solutions matching: $query"
|
|
4681
|
+
else
|
|
4682
|
+
echo ""
|
|
4683
|
+
echo " Found: ${found} solutions"
|
|
4684
|
+
fi
|
|
4685
|
+
;;
|
|
4686
|
+
|
|
4687
|
+
run)
|
|
4688
|
+
echo -e "${BOLD}Compounding learnings into solutions...${NC}"
|
|
4689
|
+
echo ""
|
|
4690
|
+
|
|
4691
|
+
local learnings_dir="${HOME}/.loki/learnings"
|
|
4692
|
+
if [ ! -d "$learnings_dir" ]; then
|
|
4693
|
+
echo " No learnings directory found at: $learnings_dir"
|
|
4694
|
+
echo " Run a Loki session first to generate learnings."
|
|
4695
|
+
return
|
|
4696
|
+
fi
|
|
4697
|
+
|
|
4698
|
+
python3 << 'COMPOUND_RUN_SCRIPT'
|
|
4699
|
+
import json, os, re
|
|
4700
|
+
from datetime import datetime, timezone
|
|
4701
|
+
from collections import defaultdict
|
|
4702
|
+
learnings_dir = os.path.expanduser("~/.loki/learnings")
|
|
4703
|
+
solutions_dir = os.path.expanduser("~/.loki/solutions")
|
|
4704
|
+
CATEGORIES = ["security", "performance", "architecture", "testing", "debugging", "deployment", "general"]
|
|
4705
|
+
CATEGORY_KEYWORDS = {
|
|
4706
|
+
"security": ["auth", "login", "password", "token", "injection", "xss", "csrf", "cors", "secret", "encrypt", "permission"],
|
|
4707
|
+
"performance": ["cache", "query", "n+1", "memory", "leak", "slow", "timeout", "pool", "index", "optimize", "bundle"],
|
|
4708
|
+
"architecture": ["pattern", "solid", "coupling", "abstraction", "module", "interface", "design", "refactor", "structure"],
|
|
4709
|
+
"testing": ["test", "mock", "fixture", "coverage", "assert", "spec", "e2e", "playwright", "jest", "flaky"],
|
|
4710
|
+
"debugging": ["debug", "error", "trace", "log", "stack", "crash", "exception", "breakpoint", "inspect"],
|
|
4711
|
+
"deployment": ["deploy", "docker", "ci", "cd", "pipeline", "kubernetes", "nginx", "ssl", "domain", "env", "config"],
|
|
4712
|
+
}
|
|
4713
|
+
def load_jsonl(filepath):
|
|
4714
|
+
entries = []
|
|
4715
|
+
if not os.path.exists(filepath): return entries
|
|
4716
|
+
with open(filepath, 'r') as f:
|
|
4717
|
+
for line in f:
|
|
4718
|
+
try:
|
|
4719
|
+
entry = json.loads(line)
|
|
4720
|
+
if 'description' in entry: entries.append(entry)
|
|
4721
|
+
except: continue
|
|
4722
|
+
return entries
|
|
4723
|
+
def classify_category(description):
|
|
4724
|
+
desc_lower = description.lower()
|
|
4725
|
+
scores = {cat: sum(1 for kw in kws if kw in desc_lower) for cat, kws in CATEGORY_KEYWORDS.items()}
|
|
4726
|
+
best = max(scores, key=scores.get)
|
|
4727
|
+
return best if scores[best] > 0 else "general"
|
|
4728
|
+
def slugify(text):
|
|
4729
|
+
return re.sub(r'[^a-z0-9]+', '-', text.lower().strip()).strip('-')[:80]
|
|
4730
|
+
def solution_exists(solutions_dir, title_slug):
|
|
4731
|
+
for cat in CATEGORIES:
|
|
4732
|
+
cat_dir = os.path.join(solutions_dir, cat)
|
|
4733
|
+
if os.path.exists(cat_dir) and os.path.exists(os.path.join(cat_dir, f"{title_slug}.md")):
|
|
4734
|
+
return True
|
|
4735
|
+
return False
|
|
4736
|
+
patterns = load_jsonl(os.path.join(learnings_dir, "patterns.jsonl"))
|
|
4737
|
+
mistakes = load_jsonl(os.path.join(learnings_dir, "mistakes.jsonl"))
|
|
4738
|
+
successes = load_jsonl(os.path.join(learnings_dir, "successes.jsonl"))
|
|
4739
|
+
print(f" Loaded: {len(patterns)} patterns, {len(mistakes)} mistakes, {len(successes)} successes")
|
|
4740
|
+
grouped = defaultdict(list)
|
|
4741
|
+
for entry in patterns + mistakes + successes:
|
|
4742
|
+
grouped[classify_category(entry.get('description', ''))].append(entry)
|
|
4743
|
+
created = 0
|
|
4744
|
+
now = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
4745
|
+
for category, entries in grouped.items():
|
|
4746
|
+
if len(entries) < 2: continue
|
|
4747
|
+
cat_dir = os.path.join(solutions_dir, category)
|
|
4748
|
+
os.makedirs(cat_dir, exist_ok=True)
|
|
4749
|
+
best_entry = max(entries, key=lambda e: len(e.get('description', '')))
|
|
4750
|
+
title = best_entry['description'][:120]
|
|
4751
|
+
slug = slugify(title)
|
|
4752
|
+
if solution_exists(solutions_dir, slug): continue
|
|
4753
|
+
all_words = ' '.join(e.get('description', '') for e in entries).lower()
|
|
4754
|
+
tags = []
|
|
4755
|
+
for kw_list in CATEGORY_KEYWORDS.values():
|
|
4756
|
+
for kw in kw_list:
|
|
4757
|
+
if kw in all_words and kw not in tags: tags.append(kw)
|
|
4758
|
+
tags = tags[:8]
|
|
4759
|
+
symptoms = [e.get('description', '')[:200] for e in entries
|
|
4760
|
+
if any(w in e.get('description', '').lower() for w in ['error', 'fail', 'bug', 'crash', 'issue'])][:4]
|
|
4761
|
+
if not symptoms: symptoms = [entries[0].get('description', '')[:200]]
|
|
4762
|
+
solution_lines = [f"- {e.get('description', '')}" for e in entries
|
|
4763
|
+
if not any(w in e.get('description', '').lower() for w in ['error', 'fail', 'bug', 'crash'])]
|
|
4764
|
+
if not solution_lines: solution_lines = [f"- {entries[0].get('description', '')}"]
|
|
4765
|
+
project = best_entry.get('project', os.path.basename(os.getcwd()))
|
|
4766
|
+
filepath = os.path.join(cat_dir, f"{slug}.md")
|
|
4767
|
+
with open(filepath, 'w') as f:
|
|
4768
|
+
f.write(f"---\ntitle: \"{title}\"\ncategory: {category}\ntags: [{', '.join(tags)}]\nsymptoms:\n")
|
|
4769
|
+
for s in symptoms: f.write(f' - "{s}"\n')
|
|
4770
|
+
f.write(f'root_cause: "Identified from {len(entries)} related learnings"\n')
|
|
4771
|
+
f.write(f'prevention: "See solution details below"\nconfidence: {min(0.5 + 0.1 * len(entries), 0.95):.2f}\n')
|
|
4772
|
+
f.write(f'source_project: "{project}"\ncreated: "{now}"\napplied_count: 0\n---\n\n')
|
|
4773
|
+
f.write("## Solution\n\n" + '\n'.join(solution_lines) + '\n\n')
|
|
4774
|
+
f.write(f"## Context\n\nCompounded from {len(entries)} learnings from project: {project}\n")
|
|
4775
|
+
created += 1
|
|
4776
|
+
print(f" Created: {category}/{slug}.md")
|
|
4777
|
+
if created > 0: print(f"\n Compounded {created} new solution files to {solutions_dir}")
|
|
4778
|
+
else: print(" No new solutions to compound (need 2+ related learnings per category)")
|
|
4779
|
+
COMPOUND_RUN_SCRIPT
|
|
4780
|
+
;;
|
|
4781
|
+
|
|
4782
|
+
stats)
|
|
4783
|
+
echo -e "${BOLD}Compound Solution Statistics${NC}"
|
|
4784
|
+
echo ""
|
|
4785
|
+
|
|
4786
|
+
if [ ! -d "$solutions_dir" ]; then
|
|
4787
|
+
echo " No solutions directory found."
|
|
4788
|
+
return
|
|
4789
|
+
fi
|
|
4790
|
+
|
|
4791
|
+
local total=0
|
|
4792
|
+
local newest=""
|
|
4793
|
+
local oldest=""
|
|
4794
|
+
|
|
4795
|
+
for category in security performance architecture testing debugging deployment general; do
|
|
4796
|
+
local cat_dir="$solutions_dir/$category"
|
|
4797
|
+
if [ -d "$cat_dir" ]; then
|
|
4798
|
+
local count=$(find "$cat_dir" -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
4799
|
+
total=$((total + count))
|
|
4800
|
+
fi
|
|
4801
|
+
done
|
|
4802
|
+
|
|
4803
|
+
echo " Total solutions: ${total}"
|
|
4804
|
+
|
|
4805
|
+
if [ "$total" -gt 0 ]; then
|
|
4806
|
+
# Find newest and oldest
|
|
4807
|
+
newest=$(find "$solutions_dir" -name "*.md" -exec stat -f '%m %N' {} \; 2>/dev/null | sort -rn | head -1 | cut -d' ' -f2-)
|
|
4808
|
+
oldest=$(find "$solutions_dir" -name "*.md" -exec stat -f '%m %N' {} \; 2>/dev/null | sort -n | head -1 | cut -d' ' -f2-)
|
|
4809
|
+
|
|
4810
|
+
if [ -n "$newest" ]; then
|
|
4811
|
+
local newest_title=$(grep '^title:' "$newest" 2>/dev/null | head -1 | sed 's/title: *"//;s/"$//')
|
|
4812
|
+
echo " Newest: ${newest_title:-$(basename "$newest" .md)}"
|
|
4813
|
+
fi
|
|
4814
|
+
if [ -n "$oldest" ]; then
|
|
4815
|
+
local oldest_title=$(grep '^title:' "$oldest" 2>/dev/null | head -1 | sed 's/title: *"//;s/"$//')
|
|
4816
|
+
echo " Oldest: ${oldest_title:-$(basename "$oldest" .md)}"
|
|
4817
|
+
fi
|
|
4818
|
+
fi
|
|
4819
|
+
|
|
4820
|
+
echo ""
|
|
4821
|
+
echo " Location: $solutions_dir"
|
|
4822
|
+
;;
|
|
4823
|
+
|
|
4824
|
+
help|--help|-h)
|
|
4825
|
+
echo -e "${BOLD}loki compound${NC} - Knowledge compounding system"
|
|
4826
|
+
echo ""
|
|
4827
|
+
echo "Extracts structured solutions from cross-project learnings."
|
|
4828
|
+
echo "Solutions are stored as markdown files with YAML frontmatter"
|
|
4829
|
+
echo "at ~/.loki/solutions/{category}/ and fed back into future planning."
|
|
4830
|
+
echo ""
|
|
4831
|
+
echo "Usage: loki compound <command>"
|
|
4832
|
+
echo ""
|
|
4833
|
+
echo "Commands:"
|
|
4834
|
+
echo " list List all solutions by category"
|
|
4835
|
+
echo " show <category> Show solutions in a category"
|
|
4836
|
+
echo " search <query> Search across all solutions"
|
|
4837
|
+
echo " run Manually compound current learnings into solutions"
|
|
4838
|
+
echo " stats Solution statistics"
|
|
4839
|
+
echo " help Show this help"
|
|
4840
|
+
echo ""
|
|
4841
|
+
echo "Categories: security, performance, architecture, testing,"
|
|
4842
|
+
echo " debugging, deployment, general"
|
|
4843
|
+
echo ""
|
|
4844
|
+
echo "Solutions are created automatically at session end and"
|
|
4845
|
+
echo "loaded during the REASON phase for relevant tasks."
|
|
4846
|
+
;;
|
|
4847
|
+
|
|
4848
|
+
*)
|
|
4849
|
+
echo -e "${RED}Unknown compound command: $subcommand${NC}"
|
|
4850
|
+
echo "Run 'loki compound help' for usage."
|
|
4851
|
+
return 1
|
|
4852
|
+
;;
|
|
4853
|
+
esac
|
|
4854
|
+
}
|
|
4855
|
+
|
|
4544
4856
|
# Completion Council management
|
|
4545
4857
|
cmd_council() {
|
|
4546
4858
|
local subcommand="${1:-status}"
|
|
@@ -5608,4 +5920,54 @@ for line in sys.stdin:
|
|
|
5608
5920
|
esac
|
|
5609
5921
|
}
|
|
5610
5922
|
|
|
5923
|
+
# Output shell completion scripts
|
|
5924
|
+
cmd_completions() {
|
|
5925
|
+
local shell="${1:-bash}"
|
|
5926
|
+
local skill_dir
|
|
5927
|
+
|
|
5928
|
+
# Find the skill directory (where autonomy/loki is located)
|
|
5929
|
+
skill_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5930
|
+
local completions_dir="$skill_dir/completions"
|
|
5931
|
+
|
|
5932
|
+
case "$shell" in
|
|
5933
|
+
bash)
|
|
5934
|
+
if [ -f "$completions_dir/loki.bash" ]; then
|
|
5935
|
+
cat "$completions_dir/loki.bash"
|
|
5936
|
+
else
|
|
5937
|
+
echo -e "${RED}Error: Bash completion script not found at $completions_dir/loki.bash${NC}" >&2
|
|
5938
|
+
exit 1
|
|
5939
|
+
fi
|
|
5940
|
+
;;
|
|
5941
|
+
zsh)
|
|
5942
|
+
if [ -f "$completions_dir/_loki" ]; then
|
|
5943
|
+
cat "$completions_dir/_loki"
|
|
5944
|
+
else
|
|
5945
|
+
echo -e "${RED}Error: Zsh completion script not found at $completions_dir/_loki${NC}" >&2
|
|
5946
|
+
exit 1
|
|
5947
|
+
fi
|
|
5948
|
+
;;
|
|
5949
|
+
*)
|
|
5950
|
+
echo -e "${BOLD}Loki Shell Completions${NC}"
|
|
5951
|
+
echo ""
|
|
5952
|
+
echo "Output shell completion scripts for bash or zsh."
|
|
5953
|
+
echo ""
|
|
5954
|
+
echo "Usage: loki completions <shell>"
|
|
5955
|
+
echo ""
|
|
5956
|
+
echo "Shells:"
|
|
5957
|
+
echo " bash Bash completion script"
|
|
5958
|
+
echo " zsh Zsh completion script"
|
|
5959
|
+
echo ""
|
|
5960
|
+
echo "Installation:"
|
|
5961
|
+
echo ""
|
|
5962
|
+
echo "Bash:"
|
|
5963
|
+
echo " eval \"\$(loki completions bash)\" >> ~/.bashrc"
|
|
5964
|
+
echo ""
|
|
5965
|
+
echo "Zsh:"
|
|
5966
|
+
echo " eval \"\$(loki completions zsh)\" >> ~/.zshrc"
|
|
5967
|
+
echo ""
|
|
5968
|
+
exit 1
|
|
5969
|
+
;;
|
|
5970
|
+
esac
|
|
5971
|
+
}
|
|
5972
|
+
|
|
5611
5973
|
main "$@"
|