frontier-council 0.1.1__py3-none-any.whl → 0.1.2__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  """Frontier Council - Multi-model deliberation for important decisions."""
2
2
 
3
- __version__ = "0.1.1"
3
+ __version__ = "0.1.2"
4
4
 
5
5
  from .council import (
6
6
  run_council,
frontier_council/cli.py CHANGED
@@ -4,11 +4,27 @@ import argparse
4
4
  import json
5
5
  import os
6
6
  import random
7
+ import re
7
8
  import subprocess
8
9
  import sys
9
10
  from datetime import datetime
10
11
  from pathlib import Path
11
12
 
13
+
14
+ def get_sessions_dir() -> Path:
15
+ """Get the sessions directory, creating if needed."""
16
+ sessions_dir = Path.home() / ".frontier-council" / "sessions"
17
+ sessions_dir.mkdir(parents=True, exist_ok=True)
18
+ return sessions_dir
19
+
20
+
21
+ def slugify(text: str, max_len: int = 40) -> str:
22
+ """Convert text to a filename-safe slug."""
23
+ text = text.lower()
24
+ text = re.sub(r'[^\w\s-]', '', text)
25
+ text = re.sub(r'[\s_-]+', '-', text)
26
+ return text[:max_len].strip('-')
27
+
12
28
  from .council import (
13
29
  COUNCIL,
14
30
  detect_social_context,
@@ -78,8 +94,33 @@ Examples:
78
94
  choices=[1, 2, 3, 4, 5],
79
95
  help="Which speaker (1-5) should be devil's advocate (default: random)",
80
96
  )
97
+ parser.add_argument(
98
+ "--no-save",
99
+ action="store_true",
100
+ help="Don't auto-save transcript to ~/.frontier-council/sessions/",
101
+ )
102
+ parser.add_argument(
103
+ "--sessions",
104
+ action="store_true",
105
+ help="List recent sessions and exit",
106
+ )
81
107
  args = parser.parse_args()
82
108
 
109
+ # Handle --sessions flag
110
+ if args.sessions:
111
+ sessions_dir = get_sessions_dir()
112
+ sessions = sorted(sessions_dir.glob("*.md"), key=lambda p: p.stat().st_mtime, reverse=True)
113
+ if not sessions:
114
+ print("No sessions found.")
115
+ else:
116
+ print(f"Sessions in {sessions_dir}:\n")
117
+ for s in sessions[:20]: # Show last 20
118
+ mtime = datetime.fromtimestamp(s.stat().st_mtime).strftime("%Y-%m-%d %H:%M")
119
+ print(f" {mtime} {s.name}")
120
+ if len(sessions) > 20:
121
+ print(f"\n ... and {len(sessions) - 20} more")
122
+ sys.exit(0)
123
+
83
124
  # Auto-detect social context if not explicitly set
84
125
  social_mode = args.social or detect_social_context(args.question)
85
126
  if social_mode and not args.social and not args.quiet:
@@ -153,12 +194,38 @@ Examples:
153
194
  print("=" * 60)
154
195
  print()
155
196
 
156
- # Save transcript
197
+ # Save transcript to user-specified location
157
198
  if args.output:
158
199
  Path(args.output).write_text(transcript)
159
200
  if not args.quiet:
160
201
  print(f"Transcript saved to: {args.output}")
161
202
 
203
+ # Auto-save to sessions directory
204
+ session_path = None
205
+ if not args.no_save:
206
+ timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
207
+ slug = slugify(args.question)
208
+ filename = f"{timestamp}-{slug}.md"
209
+ session_path = get_sessions_dir() / filename
210
+
211
+ # Build full session content with metadata header
212
+ session_content = f"""# Council Session
213
+
214
+ **Question:** {args.question}
215
+ **Date:** {datetime.now().strftime("%Y-%m-%d %H:%M")}
216
+ **Rounds:** {args.rounds}
217
+ **Mode:** {"named" if args.named else "anonymous"}, {"blind" if use_blind else "no blind"}{", social" if social_mode else ""}
218
+ {f"**Context:** {args.context}" if args.context else ""}
219
+ {f"**Persona:** {args.persona}" if args.persona else ""}
220
+
221
+ ---
222
+
223
+ {transcript}
224
+ """
225
+ session_path.write_text(session_content)
226
+ if not args.quiet:
227
+ print(f"Session saved to: {session_path}")
228
+
162
229
  # Share via gist
163
230
  gist_url = None
164
231
  if args.share:
@@ -190,10 +257,11 @@ Examples:
190
257
  print("Error: 'gh' CLI not found. Install with: brew install gh", file=sys.stderr)
191
258
 
192
259
  # Log to history
193
- history_file = Path(__file__).parent.parent / "council_history.jsonl"
260
+ history_file = get_sessions_dir().parent / "history.jsonl"
194
261
  log_entry = {
195
262
  "timestamp": datetime.now().isoformat(),
196
263
  "question": args.question[:200],
264
+ "session": str(session_path) if session_path else None,
197
265
  "gist": gist_url,
198
266
  "context": args.context,
199
267
  "rounds": args.rounds,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontier-council
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Multi-model deliberation for important decisions. 5 frontier LLMs debate, then a judge synthesizes consensus.
5
5
  Project-URL: Homepage, https://github.com/terry-li-hm/frontier-council
6
6
  Project-URL: Repository, https://github.com/terry-li-hm/frontier-council
@@ -80,8 +80,13 @@ frontier-council "Career question" --output transcript.md
80
80
 
81
81
  # Share via GitHub Gist
82
82
  frontier-council "Important decision" --share
83
+
84
+ # List past sessions
85
+ frontier-council --sessions
83
86
  ```
84
87
 
88
+ All sessions are auto-saved to `~/.frontier-council/sessions/` for later review.
89
+
85
90
  ## Options
86
91
 
87
92
  | Flag | Description |
@@ -96,6 +101,8 @@ frontier-council "Important decision" --share
96
101
  | `--persona TEXT` | Context about the person asking |
97
102
  | `--advocate N` | Which speaker (1-5) should be devil's advocate (default: random) |
98
103
  | `--quiet` | Suppress progress output |
104
+ | `--sessions` | List recent saved sessions |
105
+ | `--no-save` | Don't auto-save transcript to ~/.frontier-council/sessions/ |
99
106
 
100
107
  ## How It Works
101
108
 
@@ -0,0 +1,8 @@
1
+ frontier_council/__init__.py,sha256=VAITtl8rVW6wSN4iN86TVOmB2D2nrGu8ZI9ezDnOL3I,357
2
+ frontier_council/cli.py,sha256=9ZJgdFXHSgFDACf30veWb3uHruxlueVQY-N8hHSJL9M,9772
3
+ frontier_council/council.py,sha256=u2ir34dNostBOhXUi1R0wFEfBIEgiRX8thiS5lRFnnU,30226
4
+ frontier_council-0.1.2.dist-info/METADATA,sha256=E9iEMkdVEBtmA2NyprKIWipDAnR0gHWVGbIonlmVYrc,5229
5
+ frontier_council-0.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
6
+ frontier_council-0.1.2.dist-info/entry_points.txt,sha256=I3xjPK-nupfQz5PANVXUnXjuxlP-4-mykkA3wXhFOGY,63
7
+ frontier_council-0.1.2.dist-info/licenses/LICENSE,sha256=8qmwox7khp-AakNVvL-Ga25eYbsCtLx8RyXM4zKkX0w,1065
8
+ frontier_council-0.1.2.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- frontier_council/__init__.py,sha256=E9pFFmwBgrMqWytSCigjEnJ6sI_NpufL3FvXaYCnW1A,357
2
- frontier_council/cli.py,sha256=BbM1cHWjAA0DBn4uyGKajaNAMxhKYQt-ZGNQ6hTnlkc,7337
3
- frontier_council/council.py,sha256=u2ir34dNostBOhXUi1R0wFEfBIEgiRX8thiS5lRFnnU,30226
4
- frontier_council-0.1.1.dist-info/METADATA,sha256=43yN-RjPztdcYqeuyljSt96KboV36tjOqCn3J70RPik,4973
5
- frontier_council-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
6
- frontier_council-0.1.1.dist-info/entry_points.txt,sha256=I3xjPK-nupfQz5PANVXUnXjuxlP-4-mykkA3wXhFOGY,63
7
- frontier_council-0.1.1.dist-info/licenses/LICENSE,sha256=8qmwox7khp-AakNVvL-Ga25eYbsCtLx8RyXM4zKkX0w,1065
8
- frontier_council-0.1.1.dist-info/RECORD,,