loki-mode 6.17.1 → 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.1
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.1 | [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.1
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
  ;;
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "6.17.1"
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.1
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.1'
60
+ __version__ = '6.17.2'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "6.17.1",
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",