Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,17 @@ def get_tool_frequency(...):
expand: Include Bash→command, Skill→name, Task→subagent breakdown
"""
```

### Default Parameters

Use semantic defaults based on use case, not arbitrary consistency:

| Use Case | `days` | `limit` | Rationale |
|----------|--------|---------|-----------|
| **Pattern analysis** (sequences, frequency, trends) | `7` | `50` | Patterns need aggregated data |
| **Session lists** (list_sessions, classify, efficiency) | `7` | `20` | Sessions are large objects |
| **Recent activity** (messages, parallel) | `1` | `50` | Typically want today's context |
| **Samples** (sample_sequences) | `7` | `5` | Samples should be small |
| **Handoff context** | `0.17` | `10` | Very recent context (~4h) |

Keep MCP and CLI defaults aligned - check both `server.py` and `cli.py` when changing defaults.
13 changes: 8 additions & 5 deletions src/session_analytics/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,7 @@ def cmd_commands(args):
def cmd_sessions(args):
"""Show session info."""
storage = SQLiteStorage()
result = query_sessions(storage, days=args.days, project=args.project)
result = query_sessions(storage, days=args.days, project=args.project, limit=args.limit)
print(format_output(result, args.json))


Expand Down Expand Up @@ -1112,6 +1112,7 @@ def cmd_classify(args):
storage,
days=args.days,
project=args.project,
limit=args.limit,
)
print(format_output(result, args.json))

Expand Down Expand Up @@ -1552,6 +1553,7 @@ def main():
sub = subparsers.add_parser("sessions", help="Show session info")
sub.add_argument("--days", type=int, default=7, help="Days to analyze (default: 7)")
sub.add_argument("--project", help="Project path filter")
sub.add_argument("--limit", type=int, default=20, help="Max sessions (default: 20)")
sub.set_defaults(func=cmd_sessions)

# tokens
Expand Down Expand Up @@ -1611,7 +1613,7 @@ def main():
sub.add_argument(
"--days", type=float, default=1, help="Days to look back (default: 1, supports 0.5 for 12h)"
)
sub.add_argument("--limit", type=int, default=100, help="Max messages (default: 100)")
sub.add_argument("--limit", type=int, default=50, help="Max messages (default: 50)")
sub.add_argument("--no-projects", action="store_true", help="Exclude project info")
sub.add_argument("--session-id", help="Filter to specific session ID")
sub.add_argument(
Expand Down Expand Up @@ -1649,7 +1651,7 @@ def main():
help="Relation method (default: files)",
)
sub.add_argument("--days", type=int, default=7, help="Days to search (default: 7)")
sub.add_argument("--limit", type=int, default=10, help="Max results (default: 10)")
sub.add_argument("--limit", type=int, default=20, help="Max results (default: 20)")
sub.set_defaults(func=cmd_related)

# failures
Expand All @@ -1671,6 +1673,7 @@ def main():
sub = subparsers.add_parser("classify", help="Classify sessions by activity type")
sub.add_argument("--days", type=int, default=7, help="Days to analyze (default: 7)")
sub.add_argument("--project", help="Project filter")
sub.add_argument("--limit", type=int, default=20, help="Max sessions (default: 20)")
sub.set_defaults(func=cmd_classify)

# handoff
Expand Down Expand Up @@ -1767,7 +1770,7 @@ def main():
sub.add_argument("--days", type=int, default=7, help="Days to analyze (default: 7)")
sub.add_argument("--event-type", help="Filter by event type (e.g., 'gotcha_discovered')")
sub.add_argument("--repo", help="Filter by repo name")
sub.add_argument("--limit", type=int, default=100, help="Max events to return (default: 100)")
sub.add_argument("--limit", type=int, default=50, help="Max events to return (default: 50)")
sub.set_defaults(func=cmd_bus_events)

# Issue #69: Compaction and efficiency commands
Expand Down Expand Up @@ -1818,7 +1821,7 @@ def main():
sub = subparsers.add_parser("efficiency", help="Show session context efficiency metrics")
sub.add_argument("--days", type=int, default=7, help="Days to analyze (default: 7)")
sub.add_argument("--project", help="Project path filter")
sub.add_argument("--limit", type=int, default=50, help="Max sessions to return (default: 50)")
sub.add_argument("--limit", type=int, default=20, help="Max sessions to return (default: 20)")
sub.set_defaults(func=cmd_efficiency)

# benchmark (Issue #63)
Expand Down
10 changes: 5 additions & 5 deletions src/session_analytics/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ identify permission gaps.
| Tool | Purpose |
|------|---------|
| `get_tool_frequency(days?, project?, expand?)` | Tool usage counts with Bash/Skill/Task breakdown |
| `list_sessions(days?, project?)` | Session metadata and token totals |
| `list_sessions(days?, project?, limit?)` | Session metadata and token totals |
| `get_token_usage(days?, by?, project?)` | Token usage by day, session, or model |
| `get_session_events(days?, tool?, session_id?)` | Recent events with filtering |
| `get_session_events(start?, end?, tool?, session_id?, limit?)` | Recent events with filtering |
| `get_file_activity(days?, project?, limit?, collapse_worktrees?)` | File reads/edits/writes breakdown |
| `get_projects(days?)` | Activity across all projects |
| `get_mcp_usage(days?, project?)` | MCP server and tool usage |
Expand Down Expand Up @@ -67,7 +67,7 @@ Use `get_tool_sequences(expand=True)` to discover expanded patterns, then `sampl

| Tool | Purpose |
|------|---------|
| `classify_sessions(days?, project?)` | Categorize sessions with explanation of why |
| `classify_sessions(days?, project?, limit?)` | Categorize sessions with explanation of why |

Each session includes `classification_factors` explaining WHY it was categorized:
- `trigger`: The threshold that was exceeded (e.g., "error_rate > 15%")
Expand All @@ -94,7 +94,7 @@ Returns both core metrics (`events`, `sessions`, `errors`, `tokens`) and `effici

| Tool | Purpose |
|------|---------|
| `get_session_messages(days?, session_id?, entry_types?, max_message_length?)` | Messages across sessions chronologically (user + assistant by default) |
| `get_session_messages(days?, session_id?, limit?, entry_types?, max_message_length?)` | Messages across sessions chronologically (user + assistant by default) |
| `search_messages(query, limit?, entry_types?)` | Full-text search across all message types (FTS5) |

**entry_types**: Filter by `["user"]`, `["assistant"]`, `["tool_result"]`, `["summary"]` or any combination.
Expand All @@ -108,7 +108,7 @@ Returns both core metrics (`events`, `sessions`, `errors`, `tokens`) and `effici
| Tool | Purpose |
|------|---------|
| `detect_parallel_sessions(days?, min_overlap_minutes?)` | Find simultaneously active sessions |
| `find_related_sessions(session_id)` | Find sessions with similar patterns |
| `find_related_sessions(session_id, method?, days?, limit?)` | Find sessions with similar patterns |

### Git Integration

Expand Down
15 changes: 11 additions & 4 deletions src/session_analytics/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,13 +441,15 @@ def query_sessions(
storage: SQLiteStorage,
days: int = 7,
project: str | None = None,
limit: int = 20,
) -> dict:
"""Get session metadata.

Args:
storage: Storage instance
days: Number of days to analyze
project: Optional project path filter
limit: Maximum sessions to return

Returns:
Dict with session information
Expand All @@ -469,8 +471,9 @@ def query_sessions(
FROM sessions
WHERE {where_clause}
ORDER BY last_seen DESC
LIMIT ?
""",
params,
(*params, limit),
)

sessions = [
Expand All @@ -497,6 +500,7 @@ def query_sessions(
return {
"days": days,
"project": project,
"limit": limit,
"session_count": len(sessions),
"total_entries": total_entries,
"total_tool_uses": total_tools,
Expand Down Expand Up @@ -1066,6 +1070,7 @@ def classify_sessions(
storage: SQLiteStorage,
days: int = 7,
project: str | None = None,
limit: int = 20,
) -> dict:
"""Classify sessions based on their dominant activity patterns.

Expand All @@ -1082,6 +1087,7 @@ def classify_sessions(
storage: Storage instance
days: Number of days to analyze (default: 7)
project: Optional project filter
limit: Maximum sessions to return (default: 20)

Returns:
Dict with:
Expand Down Expand Up @@ -1284,9 +1290,10 @@ def classify_sessions(
return {
"days": days,
"project": project,
"limit": limit,
"session_count": len(classifications),
"category_distribution": category_counts,
"sessions": classifications[:50], # Limit output
"sessions": classifications[:limit],
}


Expand Down Expand Up @@ -2572,7 +2579,7 @@ def get_session_efficiency(
storage: SQLiteStorage,
days: int = 7,
project: str | None = None,
limit: int = 50,
limit: int = 20,
) -> dict:
"""Analyze session efficiency: burn rate, compactions, read patterns.

Expand All @@ -2588,7 +2595,7 @@ def get_session_efficiency(
storage: Storage instance
days: Number of days to analyze (default: 7)
project: Optional project filter
limit: Maximum sessions to return (default: 50)
limit: Maximum sessions to return (default: 20)

Returns:
Dict with efficiency metrics per session
Expand Down
26 changes: 14 additions & 12 deletions src/session_analytics/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def get_session_events(
tool: str | None = None,
project: str | None = None,
session_id: str | None = None,
limit: int = 100,
limit: int = 50,
) -> dict:
"""Get events in a time window or for a specific session.

Expand All @@ -108,7 +108,7 @@ def get_session_events(
tool: Tool name filter
project: Project path filter
session_id: Session ID filter
limit: Max events (default: 100)
limit: Max events (default: 50)
"""
from datetime import datetime

Expand All @@ -129,15 +129,16 @@ def get_session_events(


@mcp.tool()
def list_sessions(days: int = 7, project: str | None = None) -> dict:
def list_sessions(days: int = 7, project: str | None = None, limit: int = 20) -> dict:
"""List all sessions with metadata.

Args:
days: Days to analyze (default: 7)
project: Project path filter
limit: Max sessions (default: 20)
"""
queries.ensure_fresh_data(storage, days=days, project=project)
result = queries.query_sessions(storage, days=days, project=project)
result = queries.query_sessions(storage, days=days, project=project, limit=limit)
return {"status": "ok", **result}


Expand Down Expand Up @@ -248,7 +249,7 @@ def get_session_messages(
days: float = 1,
include_projects: bool = True,
session_id: str | None = None,
limit: int = 100,
limit: int = 50,
entry_types: list[str] | None = None,
max_message_length: int = 500,
) -> dict:
Expand All @@ -258,7 +259,7 @@ def get_session_messages(
days: Days to look back (default: 1, supports 0.5 for 12h)
include_projects: Include project info (default: True)
session_id: Session ID filter
limit: Max messages (default: 100)
limit: Max messages (default: 50)
entry_types: Types to include (default: ["user", "assistant"])
max_message_length: Truncate length (default: 500, 0=no limit)
"""
Expand Down Expand Up @@ -340,15 +341,15 @@ def detect_parallel_sessions(days: float = 1, min_overlap_minutes: int = 5) -> d

@mcp.tool()
def find_related_sessions(
session_id: str, method: str = "files", days: int = 7, limit: int = 10
session_id: str, method: str = "files", days: int = 7, limit: int = 20
) -> dict:
"""Find sessions related to a given session.

Args:
session_id: Session to find related sessions for
method: 'files', 'commands', or 'temporal' (default: 'files')
days: Days to search (default: 7)
limit: Max related sessions (default: 10)
limit: Max related sessions (default: 20)
"""
queries.ensure_fresh_data(storage, days=days)
result = queries.find_related_sessions(
Expand Down Expand Up @@ -403,15 +404,16 @@ def get_error_details(days: int = 7, tool: str | None = None, limit: int = 50) -


@mcp.tool()
def classify_sessions(days: int = 7, project: str | None = None) -> dict:
def classify_sessions(days: int = 7, project: str | None = None, limit: int = 20) -> dict:
"""Classify sessions by activity pattern (debugging/development/research/maintenance/mixed).

Args:
days: Days to analyze (default: 7)
project: Project filter
limit: Max sessions (default: 20)
"""
queries.ensure_fresh_data(storage, days=days)
result = queries.classify_sessions(storage, days=days, project=project)
result = queries.classify_sessions(storage, days=days, project=project, limit=limit)
return {"status": "ok", **result}


Expand Down Expand Up @@ -630,14 +632,14 @@ def get_large_tool_results(
def get_session_efficiency(
days: int = 7,
project: str | None = None,
limit: int = 50,
limit: int = 20,
) -> dict:
"""Analyze context efficiency and burn rate across sessions.

Args:
days: Days to analyze (default: 7)
project: Project path filter
limit: Max sessions (default: 50)
limit: Max sessions (default: 20)
"""
queries.ensure_fresh_data(storage, days=days)
result = queries.get_session_efficiency(storage, days=days, project=project, limit=limit)
Expand Down
3 changes: 3 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class Args:
json = False
days = 7
project = None
limit = 20

with patch("session_analytics.cli.SQLiteStorage", return_value=populated_storage):
cmd_sessions(Args())
Expand Down Expand Up @@ -580,6 +581,7 @@ class Args:
json = False
days = 7
project = None
limit = 20

with patch("session_analytics.cli.SQLiteStorage", return_value=populated_storage):
cmd_classify(Args())
Expand Down Expand Up @@ -780,6 +782,7 @@ class Args:
json = False
days = 7
project = None
limit = 20

with patch("session_analytics.cli.SQLiteStorage", return_value=storage):
cmd_sessions(Args())
Expand Down