loki-mode 6.17.0 → 6.17.2

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/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.17.0
6
+ # Loki Mode v6.17.2
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -267,4 +267,4 @@ The following features are documented in skill modules but not yet fully automat
267
267
  | Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
268
268
  | Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
269
269
 
270
- **v6.17.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
270
+ **v6.17.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 6.17.0
1
+ 6.17.2
package/autonomy/loki CHANGED
@@ -401,6 +401,7 @@ show_help() {
401
401
  echo " pause Pause after current session"
402
402
  echo " resume Resume paused execution"
403
403
  echo " status [--json] Show current status (--json for machine-readable)"
404
+ echo " stats [flags] Session statistics (--json, --efficiency)"
404
405
  echo " logs Show recent log output"
405
406
  echo " dashboard [cmd] Dashboard server (start|stop|status|url|open)"
406
407
  echo " provider [cmd] Manage AI provider (show|set|list|info)"
@@ -1713,6 +1714,293 @@ print(json.dumps(result, indent=2))
1713
1714
  fi
1714
1715
  }
1715
1716
 
1717
+ # Session statistics
1718
+ cmd_stats() {
1719
+ local show_json=false
1720
+ local show_efficiency=false
1721
+
1722
+ while [[ $# -gt 0 ]]; do
1723
+ case "$1" in
1724
+ --json) show_json=true; shift ;;
1725
+ --efficiency) show_efficiency=true; shift ;;
1726
+ *) shift ;;
1727
+ esac
1728
+ done
1729
+
1730
+ if [ ! -d "$LOKI_DIR" ]; then
1731
+ if [ "$show_json" = true ]; then
1732
+ echo '{"error": "No active session"}'
1733
+ else
1734
+ echo -e "${YELLOW}No active session found.${NC}"
1735
+ echo "Start a session with: loki start <prd>"
1736
+ fi
1737
+ return 0
1738
+ fi
1739
+
1740
+ local loki_dir="$LOKI_DIR"
1741
+ local eff_flag="$show_efficiency"
1742
+ local json_flag="$show_json"
1743
+
1744
+ python3 -c "
1745
+ import json, os, sys, glob
1746
+
1747
+ loki_dir = sys.argv[1]
1748
+ show_efficiency = sys.argv[2] == 'true'
1749
+ show_json = sys.argv[3] == 'true'
1750
+
1751
+ # --- Gather data ---
1752
+
1753
+ # Session state (orchestrator)
1754
+ phase = 'N/A'
1755
+ iteration_count = 0
1756
+ orch_file = os.path.join(loki_dir, 'state', 'orchestrator.json')
1757
+ if os.path.isfile(orch_file):
1758
+ try:
1759
+ with open(orch_file) as f:
1760
+ orch = json.load(f)
1761
+ phase = orch.get('currentPhase', 'N/A')
1762
+ iteration_count = orch.get('currentIteration', 0)
1763
+ except Exception:
1764
+ pass
1765
+
1766
+ # Per-iteration metrics
1767
+ eff_dir = os.path.join(loki_dir, 'metrics', 'efficiency')
1768
+ iterations = []
1769
+ if os.path.isdir(eff_dir):
1770
+ for path in sorted(glob.glob(os.path.join(eff_dir, 'iteration-*.json'))):
1771
+ try:
1772
+ with open(path) as f:
1773
+ iterations.append(json.load(f))
1774
+ except Exception:
1775
+ pass
1776
+
1777
+ # If we have iteration files, use their count (more accurate)
1778
+ if iterations:
1779
+ iteration_count = max(iteration_count, len(iterations))
1780
+
1781
+ total_input = sum(it.get('input_tokens', 0) for it in iterations)
1782
+ total_output = sum(it.get('output_tokens', 0) for it in iterations)
1783
+ total_tokens = total_input + total_output
1784
+ total_cost = sum(it.get('cost_usd', 0) for it in iterations)
1785
+ total_duration = sum(it.get('duration_seconds', 0) for it in iterations)
1786
+
1787
+ # Budget
1788
+ budget_limit = 0
1789
+ budget_used = 0
1790
+ budget_file = os.path.join(loki_dir, 'metrics', 'budget.json')
1791
+ if os.path.isfile(budget_file):
1792
+ try:
1793
+ with open(budget_file) as f:
1794
+ bd = json.load(f)
1795
+ budget_limit = bd.get('budget_limit', 0)
1796
+ budget_used = bd.get('budget_used', 0)
1797
+ except Exception:
1798
+ pass
1799
+
1800
+ # Quality gates
1801
+ gates_passed = 0
1802
+ gates_total = 0
1803
+ gates_file = os.path.join(loki_dir, 'state', 'quality-gates.json')
1804
+ if os.path.isfile(gates_file):
1805
+ try:
1806
+ with open(gates_file) as f:
1807
+ gates = json.load(f)
1808
+ if isinstance(gates, dict):
1809
+ for k, v in gates.items():
1810
+ if isinstance(v, dict):
1811
+ gates_total += 1
1812
+ if v.get('passed') or v.get('status') == 'passed':
1813
+ gates_passed += 1
1814
+ elif isinstance(v, bool):
1815
+ gates_total += 1
1816
+ if v:
1817
+ gates_passed += 1
1818
+ elif isinstance(gates, list):
1819
+ for g in gates:
1820
+ gates_total += 1
1821
+ if isinstance(g, dict) and (g.get('passed') or g.get('status') == 'passed'):
1822
+ gates_passed += 1
1823
+ elif g is True:
1824
+ gates_passed += 1
1825
+ except Exception:
1826
+ pass
1827
+
1828
+ # Gate failures
1829
+ gate_failures = {}
1830
+ gf_file = os.path.join(loki_dir, 'quality', 'gate-failure-count.json')
1831
+ if os.path.isfile(gf_file):
1832
+ try:
1833
+ with open(gf_file) as f:
1834
+ gate_failures = json.load(f)
1835
+ if not isinstance(gate_failures, dict):
1836
+ gate_failures = {}
1837
+ except Exception:
1838
+ pass
1839
+
1840
+ # Code reviews
1841
+ reviews_total = 0
1842
+ reviews_approved = 0
1843
+ reviews_revision = 0
1844
+ quality_dir = os.path.join(loki_dir, 'quality')
1845
+ if os.path.isdir(quality_dir):
1846
+ for fname in os.listdir(quality_dir):
1847
+ if not fname.endswith('.json') or fname == 'gate-failure-count.json':
1848
+ continue
1849
+ fpath = os.path.join(quality_dir, fname)
1850
+ try:
1851
+ with open(fpath) as f:
1852
+ rev = json.load(f)
1853
+ if isinstance(rev, dict) and ('verdict' in rev or 'approved' in rev or 'reviewers' in rev):
1854
+ reviews_total += 1
1855
+ verdict = rev.get('verdict', '').lower()
1856
+ if rev.get('approved') or verdict in ('approved', 'approve', 'pass'):
1857
+ reviews_approved += 1
1858
+ elif verdict in ('revision', 'revise', 'changes_requested', 'reject'):
1859
+ reviews_revision += 1
1860
+ except Exception:
1861
+ pass
1862
+
1863
+ # --- Format duration helper ---
1864
+ def fmt_duration(secs):
1865
+ secs = int(secs)
1866
+ if secs < 60:
1867
+ return f'{secs}s'
1868
+ hours = secs // 3600
1869
+ mins = (secs % 3600) // 60
1870
+ secs_rem = secs % 60
1871
+ if hours > 0:
1872
+ return f'{hours}h {mins:02d}m'
1873
+ return f'{mins}m {secs_rem:02d}s'
1874
+
1875
+ def fmt_number(n):
1876
+ return f'{n:,}'
1877
+
1878
+ # --- JSON output ---
1879
+ if show_json:
1880
+ output = {
1881
+ 'session': {
1882
+ 'iterations': iteration_count,
1883
+ 'duration_seconds': total_duration,
1884
+ 'phase': phase
1885
+ },
1886
+ 'tokens': {
1887
+ 'input': total_input,
1888
+ 'output': total_output,
1889
+ 'total': total_tokens,
1890
+ 'cost_usd': round(total_cost, 2)
1891
+ },
1892
+ 'quality': {
1893
+ 'gates_passed': gates_passed,
1894
+ 'gates_total': gates_total,
1895
+ 'reviews_total': reviews_total,
1896
+ 'reviews_approved': reviews_approved,
1897
+ 'reviews_revision': reviews_revision,
1898
+ 'gate_failures': gate_failures
1899
+ },
1900
+ 'efficiency': {
1901
+ 'avg_tokens_per_iteration': round(total_tokens / iteration_count) if iteration_count > 0 else 0,
1902
+ 'avg_cost_per_iteration': round(total_cost / iteration_count, 2) if iteration_count > 0 else 0,
1903
+ 'avg_duration_per_iteration': round(total_duration / iteration_count, 1) if iteration_count > 0 else 0
1904
+ },
1905
+ 'budget': {
1906
+ 'used': round(budget_used, 2),
1907
+ 'limit': budget_limit,
1908
+ 'percent': round((budget_used / budget_limit) * 100, 1) if budget_limit > 0 else 0
1909
+ }
1910
+ }
1911
+ if show_efficiency:
1912
+ output['iterations'] = []
1913
+ for i, it in enumerate(iterations, 1):
1914
+ output['iterations'].append({
1915
+ 'number': i,
1916
+ 'input_tokens': it.get('input_tokens', 0),
1917
+ 'output_tokens': it.get('output_tokens', 0),
1918
+ 'cost_usd': round(it.get('cost_usd', 0), 2),
1919
+ 'duration_seconds': it.get('duration_seconds', 0)
1920
+ })
1921
+ print(json.dumps(output, indent=2))
1922
+ sys.exit(0)
1923
+
1924
+ # --- Text output ---
1925
+ print('Loki Mode Session Statistics')
1926
+ print('============================')
1927
+ print()
1928
+
1929
+ # Session
1930
+ print('Session')
1931
+ print(f' Iterations completed: {iteration_count}')
1932
+ print(f' Duration: {fmt_duration(total_duration)}')
1933
+ print(f' Current phase: {phase}')
1934
+ print()
1935
+
1936
+ # Token Usage
1937
+ print('Token Usage')
1938
+ if iterations:
1939
+ print(f' Input tokens: {fmt_number(total_input)}')
1940
+ print(f' Output tokens: {fmt_number(total_output)}')
1941
+ print(f' Total tokens: {fmt_number(total_tokens)}')
1942
+ print(f' Estimated cost: \${total_cost:.2f}')
1943
+ else:
1944
+ print(' N/A (no iteration metrics found)')
1945
+ print()
1946
+
1947
+ # Quality Gates
1948
+ print('Quality Gates')
1949
+ if gates_total > 0:
1950
+ pct = round((gates_passed / gates_total) * 100) if gates_total > 0 else 0
1951
+ print(f' Gates passed: {gates_passed}/{gates_total} ({pct}%)')
1952
+ else:
1953
+ print(' Gates passed: N/A')
1954
+ if reviews_total > 0:
1955
+ parts = []
1956
+ if reviews_approved > 0:
1957
+ parts.append(f'{reviews_approved} approved')
1958
+ if reviews_revision > 0:
1959
+ parts.append(f'{reviews_revision} revision requested')
1960
+ detail = ', '.join(parts) if parts else 'N/A'
1961
+ print(f' Code reviews: {reviews_total} ({detail})')
1962
+ if gate_failures:
1963
+ failure_parts = [f'{k} ({v})' for k, v in gate_failures.items() if v > 0]
1964
+ if failure_parts:
1965
+ print(f' Gate failures: {', '.join(failure_parts)}')
1966
+ print()
1967
+
1968
+ # Efficiency
1969
+ print('Efficiency')
1970
+ if iteration_count > 0 and iterations:
1971
+ avg_tokens = round(total_tokens / iteration_count)
1972
+ avg_cost = total_cost / iteration_count
1973
+ avg_dur = total_duration / iteration_count
1974
+ print(f' Avg tokens/iteration: {fmt_number(avg_tokens)}')
1975
+ print(f' Avg cost/iteration: \${avg_cost:.2f}')
1976
+ print(f' Avg duration/iteration: {fmt_duration(avg_dur)}')
1977
+ else:
1978
+ print(' N/A (no iteration metrics found)')
1979
+ print()
1980
+
1981
+ # Budget
1982
+ print('Budget')
1983
+ if budget_limit > 0:
1984
+ pct = round((budget_used / budget_limit) * 100, 1)
1985
+ print(f' Used: \${budget_used:.2f} / \${budget_limit:.2f} ({pct}%)')
1986
+ elif budget_used > 0:
1987
+ print(f' Used: \${budget_used:.2f} (no limit set)')
1988
+ else:
1989
+ print(' N/A')
1990
+
1991
+ # Per-iteration breakdown
1992
+ if show_efficiency and iterations:
1993
+ print()
1994
+ print('Per-Iteration Breakdown')
1995
+ for i, it in enumerate(iterations, 1):
1996
+ inp = fmt_number(it.get('input_tokens', 0))
1997
+ out = fmt_number(it.get('output_tokens', 0))
1998
+ cost = it.get('cost_usd', 0)
1999
+ dur = fmt_duration(it.get('duration_seconds', 0))
2000
+ print(f' #{i:<3} input: {inp:<10} output: {out:<10} cost: \${cost:.2f} time: {dur}')
2001
+ " "$loki_dir" "$eff_flag" "$json_flag"
2002
+ }
2003
+
1716
2004
  # Provider management
1717
2005
  cmd_provider() {
1718
2006
  local subcommand="${1:-show}"
@@ -8053,6 +8341,9 @@ main() {
8053
8341
  status)
8054
8342
  cmd_status "$@"
8055
8343
  ;;
8344
+ stats)
8345
+ cmd_stats "$@"
8346
+ ;;
8056
8347
  dashboard)
8057
8348
  cmd_dashboard "$@"
8058
8349
  ;;
package/autonomy/run.sh CHANGED
@@ -8617,6 +8617,8 @@ if __name__ == "__main__":
8617
8617
  fi
8618
8618
  fi
8619
8619
 
8620
+ log_step "Post-iteration: running inter-iteration checks..."
8621
+
8620
8622
  # App Runner: restart on code changes (v5.45.0)
8621
8623
  if [ "${APP_RUNNER_INITIALIZED:-}" = "true" ] && type app_runner_should_restart &>/dev/null; then
8622
8624
  if app_runner_should_restart; then
@@ -8661,10 +8663,12 @@ if __name__ == "__main__":
8661
8663
  create_checkpoint "iteration-${ITERATION_COUNT} complete" "iteration-${ITERATION_COUNT}"
8662
8664
 
8663
8665
  # Quality gates (v6.10.0 - escalation ladder)
8666
+ log_step "Post-iteration: running quality gates..."
8664
8667
  local gate_failures=""
8665
8668
  if [ "${LOKI_HARD_GATES:-true}" = "true" ]; then
8666
8669
  # Static analysis gate
8667
8670
  if [ "${PHASE_STATIC_ANALYSIS:-true}" = "true" ]; then
8671
+ log_info "Quality gate: static analysis..."
8668
8672
  if enforce_static_analysis; then
8669
8673
  clear_gate_failure "static_analysis"
8670
8674
  else
@@ -8676,6 +8680,7 @@ if __name__ == "__main__":
8676
8680
  fi
8677
8681
  # Test coverage gate
8678
8682
  if [ "${PHASE_UNIT_TESTS:-true}" = "true" ]; then
8683
+ log_info "Quality gate: test coverage..."
8679
8684
  if enforce_test_coverage; then
8680
8685
  clear_gate_failure "test_coverage"
8681
8686
  else
@@ -8687,6 +8692,7 @@ if __name__ == "__main__":
8687
8692
  fi
8688
8693
  # Code review gate (upgraded from advisory, with escalation)
8689
8694
  if [ "$PHASE_CODE_REVIEW" = "true" ] && [ "$ITERATION_COUNT" -gt 0 ]; then
8695
+ log_info "Quality gate: code review..."
8690
8696
  if run_code_review; then
8691
8697
  clear_gate_failure "code_review"
8692
8698
  else
@@ -8717,9 +8723,11 @@ if __name__ == "__main__":
8717
8723
  fi
8718
8724
  else
8719
8725
  if [ "$PHASE_CODE_REVIEW" = "true" ] && [ "$ITERATION_COUNT" -gt 0 ]; then
8726
+ log_info "Quality gate: code review (advisory)..."
8720
8727
  run_code_review || log_warn "Code review found issues - check .loki/quality/reviews/"
8721
8728
  fi
8722
8729
  fi
8730
+ log_info "Quality gates complete."
8723
8731
 
8724
8732
  # Automatic episode capture after every RARV iteration (v6.15.0)
8725
8733
  # Captures RARV phase, git changes, and iteration context automatically
@@ -8745,6 +8753,7 @@ if __name__ == "__main__":
8745
8753
 
8746
8754
  # Completion Council check (v5.25.0) - multi-agent voting on completion
8747
8755
  # Runs before completion promise check since council is more comprehensive
8756
+ log_step "Post-iteration: checking completion council..."
8748
8757
  if type council_should_stop &>/dev/null && council_should_stop; then
8749
8758
  echo ""
8750
8759
  log_header "COMPLETION COUNCIL: PROJECT COMPLETE"
@@ -8776,7 +8785,7 @@ if __name__ == "__main__":
8776
8785
  fi
8777
8786
 
8778
8787
  # SUCCESS exit - continue IMMEDIATELY to next iteration (no wait!)
8779
- log_info "Iteration complete. Continuing to next iteration..."
8788
+ log_step "Starting next iteration..."
8780
8789
  ((retry++))
8781
8790
  continue # Immediately start next iteration, no exponential backoff
8782
8791
  fi
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "6.17.0"
10
+ __version__ = "6.17.2"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v6.17.0
5
+ **Version:** v6.17.2
6
6
 
7
7
  ---
8
8
 
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '6.17.0'
60
+ __version__ = '6.17.2'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "6.17.0",
3
+ "version": "6.17.2",
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",