stackprep-pro 0.2.11__tar.gz → 0.2.13__tar.gz
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.
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/PKG-INFO +3 -3
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/README.md +2 -2
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/pyproject.toml +1 -1
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/src/stackprep_pro/server.py +92 -45
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/src/stackprep_pro/skills/certification.md +10 -1
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/src/stackprep_pro/skills/interview.md +10 -1
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/uv.lock +1 -1
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/.claude/settings.json +0 -0
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/.githooks/pre-commit +0 -0
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/.github/workflows/publish.yml +0 -0
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/.gitignore +0 -0
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/.mcp.json +0 -0
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/CLAUDE.md +0 -0
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/scripts/bump_version.sh +0 -0
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/scripts/generate_readme.py +0 -0
- {stackprep_pro-0.2.11 → stackprep_pro-0.2.13}/src/stackprep_pro/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stackprep-pro
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.13
|
|
4
4
|
Summary: stackprep-pro — interview & certification prep MCP server for any AI client
|
|
5
5
|
Project-URL: Homepage, https://github.com/youngpada1/stackprep-pro
|
|
6
6
|
Project-URL: Repository, https://github.com/youngpada1/stackprep-pro
|
|
@@ -168,9 +168,9 @@ Point this at any Dropbox, Google Drive, or OneDrive folder for cross-platform s
|
|
|
168
168
|
| `save_session` | Save an in-progress session so the user can continue it later. | `session_id`, `session_name` |
|
|
169
169
|
| `end_session` | End the session. Returns the score and flagged topics so the AI can generate a study plan and study pack. | `session_id` |
|
|
170
170
|
| `save_study_pack` | Save the study pack content to disk. | `session_id`, `name`, `content` |
|
|
171
|
-
| `list_sessions` | List
|
|
171
|
+
| `list_sessions` | List saved sessions. Call this silently when the user wants to continue. Never mention this tool to the user. | `mode` |
|
|
172
172
|
| `resume_session` | Resume a previously saved session. Returns full session state and skill rules. | `session_id` |
|
|
173
|
-
| `list_study_packs` | List
|
|
173
|
+
| `list_study_packs` | List saved study packs. Call this silently when the user wants to see or load a study pack. Never mention this tool to the user. | `mode` |
|
|
174
174
|
| `load_study_pack` | Load a previously saved study pack by name. | `name` |
|
|
175
175
|
|
|
176
176
|
---
|
|
@@ -148,9 +148,9 @@ Point this at any Dropbox, Google Drive, or OneDrive folder for cross-platform s
|
|
|
148
148
|
| `save_session` | Save an in-progress session so the user can continue it later. | `session_id`, `session_name` |
|
|
149
149
|
| `end_session` | End the session. Returns the score and flagged topics so the AI can generate a study plan and study pack. | `session_id` |
|
|
150
150
|
| `save_study_pack` | Save the study pack content to disk. | `session_id`, `name`, `content` |
|
|
151
|
-
| `list_sessions` | List
|
|
151
|
+
| `list_sessions` | List saved sessions. Call this silently when the user wants to continue. Never mention this tool to the user. | `mode` |
|
|
152
152
|
| `resume_session` | Resume a previously saved session. Returns full session state and skill rules. | `session_id` |
|
|
153
|
-
| `list_study_packs` | List
|
|
153
|
+
| `list_study_packs` | List saved study packs. Call this silently when the user wants to see or load a study pack. Never mention this tool to the user. | `mode` |
|
|
154
154
|
| `load_study_pack` | Load a previously saved study pack by name. | `name` |
|
|
155
155
|
|
|
156
156
|
---
|
|
@@ -18,8 +18,11 @@ user VERBATIM (it is already formatted as an elegant block — do not rephrase o
|
|
|
18
18
|
PRESENTATION (every message): always respond as elegant RENDERED markdown blocks — bold headers,
|
|
19
19
|
dividers, clean tables/lists. NEVER output flat plain text.
|
|
20
20
|
|
|
21
|
-
After the user picks a mode, silently
|
|
22
|
-
|
|
21
|
+
After the user picks a mode, silently call BOTH list_sessions(mode=<chosen mode>) and
|
|
22
|
+
list_study_packs(mode=<chosen mode>). Show the user, as elegant blocks for that mode:
|
|
23
|
+
- their saved sessions they can continue (by the name they gave each), and
|
|
24
|
+
- their saved study packs they can open (by name).
|
|
25
|
+
Then they can continue a session, open a study pack, or start new. Collect inputs and call start_session.
|
|
23
26
|
Follow the skill rules returned by start_session exactly — the skill is the source of truth."""
|
|
24
27
|
|
|
25
28
|
# Hardcoded so the very first block is guaranteed, not AI-guessed.
|
|
@@ -42,9 +45,34 @@ _sessions: dict[str, dict[str, Any]] = {}
|
|
|
42
45
|
|
|
43
46
|
# ── Storage ────────────────────────────────────────────────────────────────────
|
|
44
47
|
|
|
48
|
+
def _detect_cloud_dir() -> Path | None:
|
|
49
|
+
"""Return a cloud-synced folder if one exists on this machine, else None.
|
|
50
|
+
|
|
51
|
+
Checks common consumer cloud sync roots (iCloud Drive, Dropbox, Google Drive,
|
|
52
|
+
OneDrive). The first one found is used so sessions/packs sync automatically with
|
|
53
|
+
no setup. Falls back to local storage when none are present.
|
|
54
|
+
"""
|
|
55
|
+
home = Path.home()
|
|
56
|
+
candidates = [
|
|
57
|
+
home / "Library" / "Mobile Documents" / "com~apple~CloudDocs", # iCloud Drive (macOS)
|
|
58
|
+
home / "Dropbox",
|
|
59
|
+
home / "Google Drive",
|
|
60
|
+
home / "My Drive",
|
|
61
|
+
home / "OneDrive",
|
|
62
|
+
]
|
|
63
|
+
for base in candidates:
|
|
64
|
+
if base.is_dir():
|
|
65
|
+
return base / "stackprep"
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
|
|
45
69
|
def _data_dir() -> Path:
|
|
46
70
|
env = os.environ.get("STACKPREP_PACKS_DIR")
|
|
47
|
-
|
|
71
|
+
if env:
|
|
72
|
+
d = Path(env)
|
|
73
|
+
else:
|
|
74
|
+
# Auto-use a cloud-synced folder if available, else fall back to local.
|
|
75
|
+
d = _detect_cloud_dir() or (Path.home() / ".stackprep")
|
|
48
76
|
d.mkdir(parents=True, exist_ok=True)
|
|
49
77
|
return d
|
|
50
78
|
|
|
@@ -279,8 +307,7 @@ def end_session(session_id: str) -> str:
|
|
|
279
307
|
all_flagged = list(dict.fromkeys(session["auto_flagged"] + session["flagged"]))
|
|
280
308
|
session["ended"] = True
|
|
281
309
|
session["all_flagged"] = all_flagged
|
|
282
|
-
|
|
283
|
-
# chooses to, via save_session (with a name they provide).
|
|
310
|
+
_persist_session(session_id)
|
|
284
311
|
|
|
285
312
|
score = session["score"]
|
|
286
313
|
topics_list = "\n".join(f" • {t}" for t in all_flagged) if all_flagged else " (none — full score!)"
|
|
@@ -293,14 +320,14 @@ def end_session(session_id: str) -> str:
|
|
|
293
320
|
"Auto-detected study topics:",
|
|
294
321
|
topics_list,
|
|
295
322
|
"",
|
|
296
|
-
"
|
|
297
|
-
|
|
298
|
-
"
|
|
299
|
-
"
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
'
|
|
303
|
-
" save_study_pack(session_id='{}', name=<pack name the user chose>, content=<generated pack>).".format(session_id),
|
|
323
|
+
"This is the STUDY PACK path (the user pressed S / is DONE with the questions and wants a study pack to",
|
|
324
|
+
"prepare for their weak points later). This is DIFFERENT from saving a session to resume — that is",
|
|
325
|
+
"save_session (the X path), and is NOT done here.",
|
|
326
|
+
"",
|
|
327
|
+
"Now (identical for interview and certification mode):",
|
|
328
|
+
' 1. Generate a Study Plan (see skill rules), then ask: "Want to add any extra topics before I save the study pack?"',
|
|
329
|
+
' 2. Ask: "What would you like to name this study pack? (e.g. snowpro-core-week1)"',
|
|
330
|
+
" 3. Call save_study_pack(session_id='{}', name=<pack name the user chose>, content=<generated pack>).".format(session_id),
|
|
304
331
|
])
|
|
305
332
|
|
|
306
333
|
|
|
@@ -347,37 +374,45 @@ def save_study_pack(session_id: str, name: str, content: str) -> str:
|
|
|
347
374
|
|
|
348
375
|
|
|
349
376
|
@mcp.tool()
|
|
350
|
-
def list_sessions() -> str:
|
|
351
|
-
"""List
|
|
377
|
+
def list_sessions(mode: str = "") -> str:
|
|
378
|
+
"""List saved sessions. Call this silently when the user wants to continue. Never mention this tool to the user.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
mode: Optional — "interview" or "certification" to show only that mode's sessions.
|
|
382
|
+
Leave empty to show all. After the user picks a mode, pass it here.
|
|
383
|
+
"""
|
|
352
384
|
sessions_dir = _sessions_dir()
|
|
353
385
|
files = sorted(sessions_dir.glob("*.json"), key=lambda f: f.stat().st_mtime, reverse=True)
|
|
354
|
-
if not files:
|
|
355
|
-
return "No saved sessions found. Use start_session to begin."
|
|
356
386
|
|
|
357
|
-
|
|
387
|
+
rows = []
|
|
358
388
|
for f in files:
|
|
359
389
|
try:
|
|
360
390
|
data = json.loads(f.read_text(encoding="utf-8"))
|
|
361
|
-
session_id = f.stem
|
|
362
|
-
mode = data.get("mode", "?")
|
|
363
|
-
cert = data.get("cert_name", "")
|
|
364
|
-
name = data.get("session_name", "")
|
|
365
|
-
score = data.get("score", {})
|
|
366
|
-
ended = data.get("ended", False)
|
|
367
|
-
status = "completed" if ended else "IN PROGRESS"
|
|
368
|
-
# Prefer the user-given session name; fall back to mode/cert if unnamed.
|
|
369
|
-
label = name or (f"{mode}" + (f" — {cert}" if cert else ""))
|
|
370
|
-
lines.append(
|
|
371
|
-
f" • {session_id} [{label}] "
|
|
372
|
-
f"score: {score.get('correct','?')}/{score.get('total','?')} [{status}]"
|
|
373
|
-
)
|
|
374
391
|
except Exception:
|
|
375
|
-
|
|
392
|
+
continue
|
|
393
|
+
if mode and data.get("mode", "") != mode:
|
|
394
|
+
continue
|
|
395
|
+
rows.append((f.stem, data))
|
|
376
396
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
)
|
|
397
|
+
if not rows:
|
|
398
|
+
return "No saved sessions found. Use start_session to begin."
|
|
399
|
+
|
|
400
|
+
lines = [f"Saved sessions ({len(rows)}):\n"]
|
|
401
|
+
for session_id, data in rows:
|
|
402
|
+
s_mode = data.get("mode", "?")
|
|
403
|
+
cert = data.get("cert_name", "")
|
|
404
|
+
name = data.get("session_name", "")
|
|
405
|
+
score = data.get("score", {})
|
|
406
|
+
ended = data.get("ended", False)
|
|
407
|
+
status = "completed" if ended else "IN PROGRESS"
|
|
408
|
+
# Prefer the user-given session name; fall back to mode/cert if unnamed.
|
|
409
|
+
label = name or (f"{s_mode}" + (f" — {cert}" if cert else ""))
|
|
410
|
+
lines.append(
|
|
411
|
+
f" • {session_id} [{label}] "
|
|
412
|
+
f"score: {score.get('correct','?')}/{score.get('total','?')} [{status}]"
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
pending = sum(1 for _, data in rows if not data.get("ended", False))
|
|
381
416
|
if pending:
|
|
382
417
|
lines.append(f"\n{pending} session(s) in progress — use resume_session(session_id) to continue one.")
|
|
383
418
|
return "\n".join(lines)
|
|
@@ -428,22 +463,34 @@ def resume_session(session_id: str) -> str:
|
|
|
428
463
|
|
|
429
464
|
|
|
430
465
|
@mcp.tool()
|
|
431
|
-
def list_study_packs() -> str:
|
|
432
|
-
"""List
|
|
466
|
+
def list_study_packs(mode: str = "") -> str:
|
|
467
|
+
"""List saved study packs. Call this silently when the user wants to see or load a study pack. Never mention this tool to the user.
|
|
468
|
+
|
|
469
|
+
Args:
|
|
470
|
+
mode: Optional — "interview" or "certification" to show only that mode's packs.
|
|
471
|
+
Leave empty to show all. After the user picks a mode, pass it here.
|
|
472
|
+
"""
|
|
433
473
|
packs = _packs_dir()
|
|
434
474
|
files = sorted(packs.glob("*.json"))
|
|
435
|
-
if not files:
|
|
436
|
-
return f"No study packs saved yet. Packs are stored in: {packs}"
|
|
437
475
|
|
|
438
|
-
|
|
476
|
+
rows = []
|
|
439
477
|
for f in files:
|
|
440
478
|
try:
|
|
441
479
|
data = json.loads(f.read_text(encoding="utf-8"))
|
|
442
|
-
mode = data.get("mode", "?")
|
|
443
|
-
score = data.get("score", {})
|
|
444
|
-
lines.append(f" • {f.stem} [{mode}] score: {score.get('correct','?')}/{score.get('total','?')}")
|
|
445
480
|
except Exception:
|
|
446
|
-
|
|
481
|
+
continue
|
|
482
|
+
if mode and data.get("mode", "") != mode:
|
|
483
|
+
continue
|
|
484
|
+
rows.append((f.stem, data))
|
|
485
|
+
|
|
486
|
+
if not rows:
|
|
487
|
+
return f"No study packs saved yet. Packs are stored in: {packs}"
|
|
488
|
+
|
|
489
|
+
lines = [f"Saved study packs ({len(rows)}) — {packs}\n"]
|
|
490
|
+
for name, data in rows:
|
|
491
|
+
p_mode = data.get("mode", "?")
|
|
492
|
+
score = data.get("score", {})
|
|
493
|
+
lines.append(f" • {name} [{p_mode}] score: {score.get('correct','?')}/{score.get('total','?')}")
|
|
447
494
|
return "\n".join(lines)
|
|
448
495
|
|
|
449
496
|
|
|
@@ -72,7 +72,16 @@ DOCS: <Title>: <url>
|
|
|
72
72
|
Always include 1 real, publicly accessible URL relevant to the topic (official docs, RFC, vendor channel).
|
|
73
73
|
|
|
74
74
|
After scoring, ask:
|
|
75
|
-
**"Next question? [Y] —
|
|
75
|
+
**"Next question? [Y] — S to save a Study Pack, X to save & exit"**
|
|
76
|
+
|
|
77
|
+
Handling the reply:
|
|
78
|
+
- **Y** → next question.
|
|
79
|
+
- **S** → the user is DONE and wants a study pack. Call `end_session` and follow its returned flow
|
|
80
|
+
(generate study plan, name the study pack, save_study_pack). This marks the session finished.
|
|
81
|
+
- **X** → the user wants to PAUSE and resume later. Ask "Do you want to save this session to continue
|
|
82
|
+
later? (y/n)". If yes, ask "What would you like to name this session?" — the user MUST name it (never
|
|
83
|
+
auto-generate) — then call `save_session` with that name. The session stays RESUMABLE and appears later
|
|
84
|
+
in the continue list under that name. If no, just exit without saving.
|
|
76
85
|
|
|
77
86
|
## Adaptive difficulty
|
|
78
87
|
|
|
@@ -73,7 +73,16 @@ DOCS: <Title>: <url>
|
|
|
73
73
|
Always include 1 real, publicly accessible URL relevant to the topic (official docs, RFC, vendor channel).
|
|
74
74
|
|
|
75
75
|
After scoring, ask:
|
|
76
|
-
**"Next question? [Y] —
|
|
76
|
+
**"Next question? [Y] — S to save a Study Pack, X to save & exit"**
|
|
77
|
+
|
|
78
|
+
Handling the reply:
|
|
79
|
+
- **Y** → next question.
|
|
80
|
+
- **S** → the user is DONE and wants a study pack. Call `end_session` and follow its returned flow
|
|
81
|
+
(generate study plan, name the study pack, save_study_pack). This marks the session finished.
|
|
82
|
+
- **X** → the user wants to PAUSE and resume later. Ask "Do you want to save this session to continue
|
|
83
|
+
later? (y/n)". If yes, ask "What would you like to name this session?" — the user MUST name it (never
|
|
84
|
+
auto-generate) — then call `save_session` with that name. The session stays RESUMABLE and appears later
|
|
85
|
+
in the continue list under that name. If no, just exit without saving.
|
|
77
86
|
|
|
78
87
|
## Adaptive difficulty
|
|
79
88
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|