@aethrekh/pi-cs 0.2.0-beta.1 → 0.3.0-beta.0

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/CHANGELOG.md CHANGED
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.3.0-beta.0](https://github.com/AshishBagdane/pi-cs/compare/v0.2.0...v0.3.0-beta.0) (2026-06-08)
11
+
12
+ ### ### Added
13
+
14
+ * documentation refresh and SEMESTER.md robustness (UX items 2–4) ([24a3026](https://github.com/AshishBagdane/pi-cs/commit/24a30268c33b13f2999a11e8be2a5d004d83348f))
15
+ * **gatekeeper:** implement UX improvements for first-time users ([7cfa3a4](https://github.com/AshishBagdane/pi-cs/commit/7cfa3a477879dacb0d6efa1ccaa1c36705690dae))
16
+ * **gatekeeper:** register skill stubs to redirect inactive-workspace users ([8bab072](https://github.com/AshishBagdane/pi-cs/commit/8bab072670e73fcf3d96b3ec4e3ecfb12fbbd56e))
17
+ * progressive disclosure, welcome tour, and custom workspace paths (UX items 5–7) ([d9af688](https://github.com/AshishBagdane/pi-cs/commit/d9af6884f18e75d0255697092c383e85cf03388d))
18
+
19
+ ### ### Fixed
20
+
21
+ * **gatekeeper:** show /skill:<name> correction when workspace is active ([05d7ce6](https://github.com/AshishBagdane/pi-cs/commit/05d7ce6b82368a37decb9854ca5d71ada138425c))
22
+ * update all skill invocations to /skill:<name> format ([86471ed](https://github.com/AshishBagdane/pi-cs/commit/86471ed3b38974961f1b9045fd8c13c84aed9e3b))
23
+
24
+ ### ### Changed
25
+
26
+ * fix ToC anchors, folder layout comment, --status output, and project structure ([f82e9f8](https://github.com/AshishBagdane/pi-cs/commit/f82e9f8c383c88d251aa43f46c7fb54b30e349e5)), closes [#skillsemester](https://github.com/AshishBagdane/pi-cs/issues/skillsemester)
27
+ * update CLAUDE.md for v0.2.0 workspace activation changes ([0fb8b85](https://github.com/AshishBagdane/pi-cs/commit/0fb8b8503e3a382344919790e3e53f6f51cb3273))
28
+
29
+ ## [0.2.0](https://github.com/AshishBagdane/pi-cs/compare/v0.2.0-beta.1...v0.2.0) (2026-06-06)
30
+
31
+ ### ### Changed
32
+
33
+ * correct six technical inaccuracies in README and STUDENT_GUIDE ([37f0d7c](https://github.com/AshishBagdane/pi-cs/commit/37f0d7c055ba7bee822a53131e9cb139fe5a8457))
34
+
10
35
  ## [0.2.0-beta.1](https://github.com/AshishBagdane/pi-cs/compare/v0.2.0-beta.0...v0.2.0-beta.1) (2026-06-06)
11
36
 
12
37
  ### ### Fixed
package/README.md CHANGED
@@ -16,14 +16,14 @@ It covers every dimension of a CS degree:
16
16
  | Need | Command | What it does |
17
17
  |---|---|---|
18
18
  | Manage workspace activation | `/pisces` | Activate, deactivate, or check workspace status |
19
- | Set up semester context | `/semester` | Init, update, or inspect your `SEMESTER.md` context |
20
- | Stuck on an assignment | `/homework` | Hints, pseudocode, guided walkthrough — no cheating |
21
- | Starting a new project | `/project` | Requirements → architecture → scaffolded codebase |
22
- | Code review before submitting | `/review` | Strict TA-level review with severity tiers |
23
- | Don't understand a concept | `/explain` | Feynman-style: analogy + diagram + code example |
24
- | Interview/LeetCode prep | `/leetcode` | Optimized solutions with complexity proofs |
25
- | Exam in 3 days | `/exam` | Quiz mode, mind maps, and revision plans |
26
- | Research paper / thesis | `/research` | Paper summaries, literature surveys, citations |
19
+ | Set up semester context | `/skill:semester` | Init, update, or inspect your `SEMESTER.md` context |
20
+ | Stuck on an assignment | `/skill:homework` | Hints, pseudocode, guided walkthrough — no cheating |
21
+ | Starting a new project | `/skill:project` | Requirements → architecture → scaffolded codebase |
22
+ | Code review before submitting | `/skill:review` | Strict TA-level review with severity tiers |
23
+ | Don't understand a concept | `/skill:explain` | Feynman-style: analogy + diagram + code example |
24
+ | Interview/LeetCode prep | `/skill:leetcode` | Optimized solutions with complexity proofs |
25
+ | Exam in 3 days | `/skill:exam` | Quiz mode, mind maps, and revision plans |
26
+ | Research paper / thesis | `/skill:research` | Paper summaries, literature surveys, citations |
27
27
 
28
28
  ---
29
29
 
@@ -39,12 +39,12 @@ pi
39
39
  /pisces --activate
40
40
 
41
41
  # 3. Initialize your semester context
42
- /semester --init
42
+ /skill:semester --init
43
43
 
44
44
  # 4. Start working
45
- /homework
46
- /explain binary search trees
47
- /leetcode 42
45
+ /skill:homework
46
+ /skill:explain binary search trees
47
+ /skill:leetcode 42
48
48
  ```
49
49
 
50
50
  After step 2, Pisces activates in `~/university` and all its subdirectories. Outside that workspace, Pi runs as stock — no skills, no persona.
@@ -76,9 +76,11 @@ Pisces walks up the directory tree from where Pi was started, up to 15 levels, s
76
76
  ### Workspace commands
77
77
 
78
78
  ```
79
- /pisces --status # show whether the workspace is active and where .pisces was found
79
+ /pisces --status # show activation state, workspace root, semester, and skills
80
80
  /pisces --activate # mark the current directory as a Pisces workspace
81
81
  /pisces --deactivate # remove .pisces from the nearest workspace above cwd
82
+ /pisces --setup # guided first-time setup: checks prerequisites, activates, prompts for semester init
83
+ /pisces --doctor # health check: Node version, workspace active, SEMESTER.md, stale files
82
84
  ```
83
85
 
84
86
  ### Switching workspace context
@@ -113,77 +115,77 @@ Pisces auto-detects this file at startup and greets you with relevant context:
113
115
 
114
116
  ## Skills Reference
115
117
 
116
- ### `/semester`
118
+ ### `/skill:semester`
117
119
  Semester context management. Three modes:
118
120
 
119
121
  ```
120
- /semester # status if SEMESTER.md exists, prompts --init if not
121
- /semester --init # scaffold a new SEMESTER.md interactively
122
- /semester --update # update week, courses, active project, or other fields
123
- /semester --status # show everything Pisces currently detects
122
+ /skill:semester # status if SEMESTER.md exists, prompts --init if not
123
+ /skill:semester --init # scaffold a new SEMESTER.md interactively
124
+ /skill:semester --update # update week, courses, active project, or other fields
125
+ /skill:semester --status # show everything Pisces currently detects
124
126
  ```
125
127
 
126
- ### `/homework`
128
+ ### `/skill:homework`
127
129
  Guided learning mode. Pisces uses the Socratic method — hints, pseudocode, and guided questions. **Never produces complete submittable solutions for graded work.**
128
130
 
129
131
  ```
130
- /homework
132
+ /skill:homework
131
133
  > Here's my problem: implement Dijkstra's algorithm...
132
134
  > I've tried a basic BFS but it doesn't handle weights
133
135
  ```
134
136
 
135
- ### `/project`
137
+ ### `/skill:project`
136
138
  Full project kickoff. Requirements analysis, architecture proposal, tech stack recommendation, and scaffolded boilerplate.
137
139
 
138
140
  ```
139
- /project
141
+ /skill:project
140
142
  > Build a REST API for a student grade tracker
141
143
  > Tech stack: open to suggestions
142
144
  > Deadline: 3 weeks
143
145
  ```
144
146
 
145
- ### `/review`
147
+ ### `/skill:review`
146
148
  TA-level code review. Covers correctness, efficiency, style, robustness, and tests. Every issue is rated (🔴 Critical / 🟠 Major / 🟡 Minor / 🔵 Suggestion).
147
149
 
148
150
  ```
149
- /review --mode=deep
151
+ /skill:review --mode=deep
150
152
  [paste your code]
151
153
  ```
152
154
 
153
- ### `/explain`
155
+ ### `/skill:explain`
154
156
  Feynman-style explanations with real-world analogies, Mermaid diagrams, and connections to your prior knowledge.
155
157
 
156
158
  ```
157
- /explain virtual memory
158
- /explain the CAP theorem --depth=advanced
159
- /explain recursion --style=analogy
159
+ /skill:explain virtual memory
160
+ /skill:explain the CAP theorem --depth=advanced
161
+ /skill:explain recursion --style=analogy
160
162
  ```
161
163
 
162
- ### `/leetcode`
164
+ ### `/skill:leetcode`
163
165
  Structured problem solving for interview prep. Full solutions with brute force → optimal progression, complexity proofs, and test cases.
164
166
 
165
167
  ```
166
- /leetcode 42
167
- /leetcode trapping rain water --lang=python
168
- /leetcode --mode=interview # simulates a real interview
168
+ /skill:leetcode 42
169
+ /skill:leetcode trapping rain water --lang=python
170
+ /skill:leetcode --mode=interview # simulates a real interview
169
171
  ```
170
172
 
171
- ### `/exam`
173
+ ### `/skill:exam`
172
174
  Exam preparation toolkit. Interactive quizzes, concept mind maps, day-by-day revision plans, and Anki-compatible flashcards.
173
175
 
174
176
  ```
175
- /exam CS301 --mode=quiz
176
- /exam operating systems --mode=mindmap
177
- /exam --mode=plan # builds schedule based on your exam date
177
+ /skill:exam CS301 --mode=quiz
178
+ /skill:exam operating systems --mode=mindmap
179
+ /skill:exam --mode=plan # builds schedule based on your exam date
178
180
  ```
179
181
 
180
- ### `/research`
182
+ ### `/skill:research`
181
183
  Academic research assistant. Paper summaries, literature surveys, citation generation, and literature review writing.
182
184
 
183
185
  ```
184
- /research "Attention is All You Need"
185
- /research attention mechanisms --mode=survey
186
- /research --mode=cite arxiv:1706.03762 --format=bibtex
186
+ /skill:research "Attention is All You Need"
187
+ /skill:research attention mechanisms --mode=survey
188
+ /skill:research --mode=cite arxiv:1706.03762 --format=bibtex
187
189
  ```
188
190
 
189
191
  ---
@@ -194,7 +196,7 @@ Pisces has academic integrity built in at every level:
194
196
 
195
197
  - ✅ Always provides hints, explanations, and pseudocode
196
198
  - ✅ Helps you debug **your own** code
197
- - ✅ Writes complete code for personal projects, `/leetcode`, and `/project`
199
+ - ✅ Writes complete code for personal projects, `/skill:leetcode`, and `/skill:project`
198
200
  - ❌ Never writes complete solutions for graded assignments
199
201
  - ❌ Never submits or helps disguise AI-generated work as your own
200
202
 
@@ -279,6 +281,7 @@ pi-cs/
279
281
  │ ├── extensions/
280
282
  │ │ ├── gatekeeper.ts # Workspace gating; /pisces command
281
283
  │ │ ├── lib/
284
+ │ │ │ ├── config.ts # Shared config loader (loadConfig, deepMerge)
282
285
  │ │ │ ├── workspace.ts # .pisces discovery (walks up 15 levels)
283
286
  │ │ │ └── workspace-state.ts # Shared activation state across extensions
284
287
  │ │ ├── semester-detector.ts # Auto-loads SEMESTER.md (workspace-gated)
@@ -300,9 +303,10 @@ pi-cs/
300
303
  ## Roadmap
301
304
 
302
305
  - [x] 8 core skills
303
- - [x] 4 background extensions
306
+ - [x] 4 background extensions + workspace gatekeeper
304
307
  - [x] Semester context system
305
308
  - [x] Academic integrity guard
309
+ - [x] Workspace-scoped activation (`.pisces` marker, `/pisces` command)
306
310
  - [ ] arXiv live search integration
307
311
  - [ ] Model routing presets
308
312
  - [ ] Obsidian vault sync
@@ -129,6 +129,17 @@
129
129
  "description": "Lightweight model for fast, simple queries"
130
130
  }
131
131
  }
132
+ },
133
+ "workspace": {
134
+ "type": "object",
135
+ "description": "Workspace discovery settings",
136
+ "properties": {
137
+ "customPaths": {
138
+ "type": "array",
139
+ "items": { "type": "string" },
140
+ "description": "Additional directories to search for SEMESTER.md (absolute paths, or ~ for home)"
141
+ }
142
+ }
132
143
  }
133
144
  }
134
145
  }
@@ -3,52 +3,148 @@
3
3
  * gatekeeper.ts
4
4
  *
5
5
  * Extension: Workspace Gatekeeper
6
- * Triggers: resources_discover
6
+ * Triggers: resources_discover, session_start
7
7
  *
8
- * Always loads. Has no academic content.
8
+ * Always loads regardless of workspace state.
9
9
  * Sole job: detect workspace state and gate all Pisces resources.
10
10
  *
11
- * Registers /pisces --activate | --deactivate | --status.
11
+ * Registers /pisces --activate | --deactivate | --status | --setup | --doctor.
12
12
  *
13
13
  * Note: Pi's cwd is fixed at session startup — the extension context does not
14
14
  * reflect mid-session directory changes. To switch workspace context, start Pi
15
15
  * in the target directory or run /pisces --activate from within a new session.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.suggestWorkspaceRoot = suggestWorkspaceRoot;
18
19
  exports.default = default_1;
19
20
  const fs_1 = require("fs");
20
21
  const path_1 = require("path");
22
+ const os_1 = require("os");
21
23
  const workspace_1 = require("./lib/workspace");
22
24
  const workspace_state_1 = require("./lib/workspace-state");
25
+ const semester_detector_1 = require("./semester-detector");
26
+ const LEGACY_APPEND_PATH = (0, path_1.join)((0, os_1.homedir)(), ".pi", "agent", "APPEND_SYSTEM.md");
27
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
28
+ /**
29
+ * Walks up from the parent of cwd looking for a university-named directory.
30
+ * Returns it if found and different from cwd, so we can suggest it as the
31
+ * workspace root instead of wherever the user happens to be running from.
32
+ */
33
+ function suggestWorkspaceRoot(cwd) {
34
+ const pattern = /^(university|uni|college|school|cs|studies|academics|fall\d*|spring\d*|summer\d*|winter\d*)\b/i;
35
+ const home = (0, path_1.resolve)((0, os_1.homedir)());
36
+ let dir = (0, path_1.dirname)(cwd);
37
+ let best = null;
38
+ while (dir.length > home.length && dir !== home) {
39
+ if (pattern.test((0, path_1.basename)(dir)))
40
+ best = dir; // overwrite to keep highest-level (closest to home) match
41
+ const parent = (0, path_1.dirname)(dir);
42
+ if (parent === dir)
43
+ break;
44
+ dir = parent;
45
+ }
46
+ return best;
47
+ }
48
+ // ─── Extension Factory ────────────────────────────────────────────────────────
23
49
  function default_1(pi) {
24
- // ─── Resource Gating ─────────────────────────────────────────────────────
50
+ // ─── Resource Gating ──────────────────────────────────────────────────────
25
51
  // resources_discover fires before session_start and before skills are
26
- // injected into the system prompt. Returning skillPaths: [] here is the
27
- // only reliable way to suppress skills outside a workspace.
28
- // Doubles as the authoritative initial sync of shared workspace state.
52
+ // injected into the system prompt. Returning skillPaths: [] is the only
53
+ // reliable way to suppress skills outside a workspace.
29
54
  pi.on("resources_discover", async (event) => {
30
55
  const { isActive } = (0, workspace_state_1.syncWorkspaceState)(event.cwd);
31
56
  if (isActive) {
32
- return {
33
- skillPaths: [(0, path_1.resolve)(__dirname, "../skills")],
34
- };
57
+ return { skillPaths: [(0, path_1.resolve)(__dirname, "../skills")] };
35
58
  }
36
59
  return { skillPaths: [] };
37
60
  });
38
- // ─── /pisces Command ─────────────────────────────────────────────────────
61
+ // ─── Skill Redirect Stubs ─────────────────────────────────────────────────
62
+ // Register every skill name as a command so that when a user types /homework
63
+ // (etc.) in an inactive workspace, they get a helpful redirect instead of Pi's
64
+ // generic "unknown command" response. When the workspace IS active the real
65
+ // skill is loaded and handles the invocation; the stub becomes a no-op.
66
+ const SKILL_NAMES = [
67
+ "homework", "project", "review", "explain",
68
+ "leetcode", "exam", "research", "semester",
69
+ ];
70
+ for (const skill of SKILL_NAMES) {
71
+ pi.registerCommand(skill, {
72
+ description: `Pisces /${skill} skill`,
73
+ handler: async (_args, ctx) => {
74
+ if ((0, workspace_state_1.getWorkspaceState)().isActive) {
75
+ ctx.ui.notify(`🐠 Use /skill:${skill} to invoke this Pisces skill.`, "info");
76
+ return;
77
+ }
78
+ ctx.ui.notify([
79
+ `🐠 Pisces: /${skill} is available but the workspace isn't active here.`,
80
+ "",
81
+ " /pisces --activate Enable skills in this directory",
82
+ " /pisces --setup Guided setup (recommended for first time)",
83
+ ].join("\n"), "info");
84
+ },
85
+ });
86
+ }
87
+ // ─── Inactive Nudge ───────────────────────────────────────────────────────
88
+ // Show a clear "installed but not active" message so users know what to do
89
+ // instead of seeing a silent, featureless session.
90
+ pi.on("session_start", async (_event, ctx) => {
91
+ if ((0, workspace_state_1.getWorkspaceState)().isActive)
92
+ return;
93
+ ctx.ui.notify([
94
+ "✓ Pisces is installed · ✗ Not active here",
95
+ "",
96
+ "Skills are off until you activate a workspace.",
97
+ "",
98
+ " /pisces --activate Mark this directory as your workspace",
99
+ " /pisces --setup Guided first-time setup (recommended)",
100
+ " /pisces --status See what Pisces detects",
101
+ ].join("\n"), "info");
102
+ });
103
+ // ─── /pisces Command ──────────────────────────────────────────────────────
39
104
  pi.registerCommand("pisces", {
40
105
  description: "Manage Pisces workspace activation",
41
106
  handler: async (args, ctx) => {
42
107
  const flag = args?.trim();
43
- // ── --activate ──────────────────────────────────────────────────────
108
+ // ── --activate ────────────────────────────────────────────────────────
44
109
  if (flag === "--activate") {
45
110
  const cwd = process.cwd();
46
- (0, fs_1.writeFileSync)((0, path_1.join)(cwd, ".pisces"), "");
47
- ctx.ui.notify(`Pisces workspace activated at ${cwd}`, "info");
111
+ let targetDir = cwd;
112
+ const suggestedRoot = suggestWorkspaceRoot(cwd);
113
+ if (suggestedRoot) {
114
+ const useParent = await ctx.ui.confirm("Suggested workspace root", [
115
+ `"${(0, path_1.basename)(suggestedRoot)}" looks like a top-level study folder.`,
116
+ "",
117
+ ` Suggested: ${suggestedRoot}`,
118
+ ` Current: ${cwd}`,
119
+ "",
120
+ "Activate at the suggested root? (Skills will work in all subdirectories.)",
121
+ ].join("\n"));
122
+ if (useParent)
123
+ targetDir = suggestedRoot;
124
+ }
125
+ const confirmed = await ctx.ui.confirm("Activate workspace?", [
126
+ "Create .pisces in:",
127
+ ` ${targetDir}`,
128
+ "",
129
+ "Skills will be available here and in every subdirectory.",
130
+ ].join("\n"));
131
+ if (!confirmed)
132
+ return;
133
+ (0, fs_1.writeFileSync)((0, path_1.join)(targetDir, ".pisces"), "");
134
+ ctx.ui.notify([
135
+ `✓ Workspace activated`,
136
+ ` ${targetDir}`,
137
+ "",
138
+ "Available skills:",
139
+ " /skill:homework /skill:project /skill:review /skill:explain",
140
+ " /skill:leetcode /skill:exam /skill:research /skill:semester",
141
+ "",
142
+ "Run /skill:semester --init to give Pisces your course context.",
143
+ ].join("\n"), "info");
48
144
  await ctx.reload();
49
145
  return;
50
146
  }
51
- // ── --deactivate ─────────────────────────────────────────────────────
147
+ // ── --deactivate ──────────────────────────────────────────────────────
52
148
  if (flag === "--deactivate") {
53
149
  const { isActive, root } = (0, workspace_1.findWorkspace)(process.cwd());
54
150
  if (!isActive || !root) {
@@ -69,37 +165,138 @@ function default_1(pi) {
69
165
  await ctx.reload();
70
166
  return;
71
167
  }
72
- // ── --status (default) ───────────────────────────────────────────────
168
+ // ── --status ──────────────────────────────────────────────────────────
73
169
  if (flag === "--status" || !flag) {
74
170
  const { isActive, root } = (0, workspace_1.findWorkspace)(process.cwd());
75
171
  if (isActive && root) {
76
172
  const cwd = process.cwd();
77
- const depth = cwd
78
- .replace(root, "")
79
- .split("/")
80
- .filter(Boolean).length;
173
+ const depth = cwd.replace(root, "").split("/").filter(Boolean).length;
174
+ const semResult = (0, semester_detector_1.detectSemesterContext)();
175
+ const semCtx = semResult.context;
176
+ const semLine = semCtx
177
+ ? `${semCtx.semester} ${semCtx.year}${semCtx.week ? `, Week ${semCtx.week}` : ""}`
178
+ : "not found — run /skill:semester --init";
179
+ const coursesLine = semCtx?.courses.length
180
+ ? semCtx.courses.map((c) => c.code).join(", ")
181
+ : "—";
81
182
  ctx.ui.notify([
82
- "Pisces Status",
83
- "─────────────────────────────",
84
- `Active: Yes`,
85
- `Workspace root: ${root}`,
86
- `.pisces found: ${depth} level${depth !== 1 ? "s" : ""} above cwd`,
87
- ].join("\n"), "info");
183
+ "Pisces Active",
184
+ "────────────────────────────────────────",
185
+ `Workspace: ${root}`,
186
+ depth > 0
187
+ ? ` (${depth} level${depth !== 1 ? "s" : ""} inside)`
188
+ : "",
189
+ `Semester: ${semLine}`,
190
+ `Courses: ${coursesLine}`,
191
+ `Project: ${semCtx?.active_project ?? "—"}`,
192
+ "────────────────────────────────────────",
193
+ "Skills: /skill:homework /skill:project /skill:review /skill:explain",
194
+ " /skill:leetcode /skill:exam /skill:research /skill:semester",
195
+ ]
196
+ .filter((l) => l !== "")
197
+ .join("\n"), "info");
88
198
  }
89
199
  else {
90
200
  ctx.ui.notify([
91
- "Pisces Status",
92
- "─────────────────────────────",
93
- `Active: No`,
94
- `Reason: No .pisces found within 15 levels or above home directory`,
201
+ "Pisces Not Active",
202
+ "────────────────────────────────────────",
203
+ "No .pisces workspace marker found.",
204
+ `(searched up from ${process.cwd()})`,
205
+ "",
206
+ "Next step",
207
+ "─────────",
208
+ "Navigate to your university folder, then run:",
95
209
  "",
96
- `Run /pisces --activate to mark this directory as a workspace.`,
210
+ " /pisces --activate Create workspace here",
211
+ " /pisces --setup Guided setup (recommended)",
97
212
  ].join("\n"), "info");
98
213
  }
99
214
  return;
100
215
  }
101
- // ── unknown flag ─────────────────────────────────────────────────────
102
- ctx.ui.notify("Usage: /pisces --activate | --deactivate | --status", "warning");
216
+ // ── --setup ───────────────────────────────────────────────────────────
217
+ if (flag === "--setup") {
218
+ const nodeVersion = process.version;
219
+ const nodeOk = parseInt(nodeVersion.slice(1).split(".")[0], 10) >= 18;
220
+ const { isActive, root } = (0, workspace_1.findWorkspace)(process.cwd());
221
+ const semResult = (0, semester_detector_1.detectSemesterContext)();
222
+ // Already fully configured — show summary and exit
223
+ if (isActive && semResult.found) {
224
+ ctx.ui.notify([
225
+ "✓ Pisces is fully configured",
226
+ "",
227
+ `Workspace: ${root}`,
228
+ semResult.context
229
+ ? `Semester: ${semResult.context.semester} ${semResult.context.year}`
230
+ : "",
231
+ "",
232
+ "You're all set! Try /skill:homework, /skill:explain, or /skill:project.",
233
+ ]
234
+ .filter((l) => l !== "")
235
+ .join("\n"), "info");
236
+ return;
237
+ }
238
+ ctx.ui.notify([
239
+ "Pisces Setup",
240
+ "════════════════════════════════════════",
241
+ "",
242
+ `${nodeOk ? "✓" : "✗"} Node.js ${nodeVersion}${nodeOk ? "" : " ← upgrade to ≥ 18 before continuing"}`,
243
+ `✓ Pisces installed`,
244
+ `${isActive ? "✓" : "✗"} Workspace${isActive ? ` active at ${root}` : " not active yet"}`,
245
+ `${isActive && !semResult.found ? "·" : ""}${!isActive ? "" : "✓"} SEMESTER.md${semResult.found ? ` found` : isActive ? " not found" : ""}`,
246
+ ]
247
+ .filter((l) => l !== "")
248
+ .join("\n"), "info");
249
+ if (!isActive) {
250
+ const activateNow = await ctx.ui.confirm("Activate workspace now?", [
251
+ "Create .pisces in:",
252
+ ` ${process.cwd()}`,
253
+ "",
254
+ "Or navigate to your university root folder first and re-run /pisces --setup.",
255
+ ].join("\n"));
256
+ if (activateNow) {
257
+ (0, fs_1.writeFileSync)((0, path_1.join)(process.cwd(), ".pisces"), "");
258
+ ctx.ui.notify([
259
+ `✓ Workspace activated at ${process.cwd()}`,
260
+ "",
261
+ "Next: run /skill:semester --init to add your course context.",
262
+ ].join("\n"), "info");
263
+ await ctx.reload();
264
+ }
265
+ return;
266
+ }
267
+ // Workspace active, but no SEMESTER.md
268
+ const doSemester = await ctx.ui.confirm("Set up semester context?", "Run /skill:semester --init to load your courses, active project, and week number?");
269
+ if (doSemester) {
270
+ ctx.ui.notify("Run: /skill:semester --init", "info");
271
+ }
272
+ return;
273
+ }
274
+ // ── --doctor ──────────────────────────────────────────────────────────
275
+ if (flag === "--doctor") {
276
+ const nodeVersion = process.version;
277
+ const nodeOk = parseInt(nodeVersion.slice(1).split(".")[0], 10) >= 18;
278
+ const { isActive, root } = (0, workspace_1.findWorkspace)(process.cwd());
279
+ const semResult = (0, semester_detector_1.detectSemesterContext)();
280
+ const staleGlobal = (0, fs_1.existsSync)(LEGACY_APPEND_PATH);
281
+ const allOk = nodeOk && isActive && !staleGlobal;
282
+ ctx.ui.notify([
283
+ "Pisces Doctor",
284
+ "════════════════════════════════════════",
285
+ "",
286
+ `${nodeOk ? "✓" : "✗"} Node.js ${nodeVersion}${nodeOk ? "" : " ← need ≥ 18"}`,
287
+ `✓ Gatekeeper loaded`,
288
+ `${isActive ? "✓" : "✗"} Workspace${isActive ? ` active (${root})` : " ← run /pisces --activate"}`,
289
+ `${semResult.found ? "✓" : "·"} SEMESTER.md${semResult.found ? ` (${semResult.source})` : " ← optional, run /skill:semester --init"}`,
290
+ `${staleGlobal ? "⚠" : "✓"} Stale APPEND_SYSTEM.md${staleGlobal
291
+ ? ` ← ${LEGACY_APPEND_PATH}\n will auto-clean on next startup`
292
+ : " (clean)"}`,
293
+ "",
294
+ allOk ? "All checks passed." : "Fix the flagged items above and restart Pi.",
295
+ ].join("\n"), "info");
296
+ return;
297
+ }
298
+ // ── unknown flag ──────────────────────────────────────────────────────
299
+ ctx.ui.notify("Usage: /pisces --activate | --deactivate | --status | --setup | --doctor", "warning");
103
300
  },
104
301
  });
105
302
  }
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ /**
3
+ * config.ts
4
+ *
5
+ * Shared config loading for Pi extensions.
6
+ *
7
+ * Lives here (not in src/index.ts) so extensions can import it without
8
+ * creating a circular dependency — src/index.ts imports from extensions,
9
+ * so extensions must not import back from src/index.ts.
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.getConfigSearchPaths = getConfigSearchPaths;
46
+ exports.deepMerge = deepMerge;
47
+ exports.loadConfig = loadConfig;
48
+ const fs = __importStar(require("fs"));
49
+ const path = __importStar(require("path"));
50
+ const os = __importStar(require("os"));
51
+ // ─── Helpers ──────────────────────────────────────────────────────────────
52
+ // Resolves from src/extensions/lib/ (dev) and dist/extensions/lib/ (compiled),
53
+ // both of which are three levels below the package root.
54
+ const DEFAULTS_PATH = path.join(__dirname, "../../../config/defaults.json");
55
+ function getConfigSearchPaths() {
56
+ return [
57
+ path.join(process.cwd(), ".pi-cs.json"),
58
+ path.join(os.homedir(), ".pi", "pi-cs.json"),
59
+ path.join(os.homedir(), ".config", "pi-cs", "config.json"),
60
+ ];
61
+ }
62
+ function deepMerge(base, override) {
63
+ const result = { ...base };
64
+ for (const key of Object.keys(override)) {
65
+ const baseVal = base[key];
66
+ const overrideVal = override[key];
67
+ if (overrideVal !== null &&
68
+ typeof overrideVal === "object" &&
69
+ !Array.isArray(overrideVal) &&
70
+ typeof baseVal === "object" &&
71
+ baseVal !== null) {
72
+ result[key] = deepMerge(baseVal, overrideVal);
73
+ }
74
+ else if (overrideVal !== undefined) {
75
+ result[key] = overrideVal;
76
+ }
77
+ }
78
+ return result;
79
+ }
80
+ // ─── Loader ────────────────────────────────────────────────────────────────
81
+ /**
82
+ * Loads and merges user config over package defaults.
83
+ * Never throws — falls back to defaults on any read/parse error.
84
+ */
85
+ function loadConfig() {
86
+ let defaults = {};
87
+ try {
88
+ const raw = fs.readFileSync(DEFAULTS_PATH, "utf-8");
89
+ defaults = JSON.parse(raw);
90
+ }
91
+ catch {
92
+ defaults = {
93
+ explanations: { default_depth: "intermediate", prefer_visuals: true, use_analogies: true },
94
+ code: { style: "balanced", always_include_complexity: true, always_include_tests: false },
95
+ integrity: { enabled: true, strictness: "balanced" },
96
+ productivity: { burnout_nudges: true, session_warning_minutes: 180, weekly_summary: true },
97
+ };
98
+ }
99
+ for (const configPath of getConfigSearchPaths()) {
100
+ try {
101
+ if (fs.existsSync(configPath)) {
102
+ const raw = fs.readFileSync(configPath, "utf-8");
103
+ const userConfig = JSON.parse(raw);
104
+ return deepMerge(defaults, userConfig);
105
+ }
106
+ }
107
+ catch {
108
+ continue;
109
+ }
110
+ }
111
+ return defaults;
112
+ }
113
+ //# sourceMappingURL=config.js.map
@@ -50,9 +50,10 @@ const fs = __importStar(require("fs"));
50
50
  const path = __importStar(require("path"));
51
51
  const os = __importStar(require("os"));
52
52
  const workspace_state_1 = require("./lib/workspace-state");
53
+ const config_1 = require("./lib/config");
53
54
  // ─── Search Paths ──────────────────────────────────────────────────────────
54
55
  function getSearchPaths() {
55
- return [
56
+ const standard = [
56
57
  process.cwd(),
57
58
  path.join(os.homedir(), "university"),
58
59
  path.join(os.homedir(), "uni"),
@@ -60,9 +61,31 @@ function getSearchPaths() {
60
61
  path.join(os.homedir(), "school"),
61
62
  path.join(os.homedir(), "Documents", "university"),
62
63
  path.join(os.homedir(), "Documents", "uni"),
64
+ path.join(os.homedir(), "Documents", "college"),
65
+ path.join(os.homedir(), "Documents", "school"),
63
66
  ];
67
+ const custom = ((0, config_1.loadConfig)().workspace?.customPaths ?? []).map((p) => p.startsWith("~") ? path.join(os.homedir(), p.slice(1)) : p);
68
+ return [...standard, ...custom];
64
69
  }
65
70
  const SEMESTER_FILE = "SEMESTER.md";
71
+ /**
72
+ * Finds SEMESTER.md in a directory, accepting any casing of the filename.
73
+ * Always reads the directory listing to get the actual on-disk filename so
74
+ * that wrong-casing detection works correctly on case-insensitive filesystems
75
+ * (e.g. macOS) where existsSync cannot distinguish 'SEMESTER.md' from 'semester.md'.
76
+ */
77
+ function findSemesterFile(dir) {
78
+ try {
79
+ const entries = fs.readdirSync(dir);
80
+ const match = entries.find((f) => f.toLowerCase() === SEMESTER_FILE.toLowerCase());
81
+ if (!match)
82
+ return null;
83
+ return { filePath: path.join(dir, match), wrongCasing: match !== SEMESTER_FILE };
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
66
89
  // ─── Parser ────────────────────────────────────────────────────────────────
67
90
  /**
68
91
  * Parses a SEMESTER.md file into a SemesterContext object.
@@ -115,20 +138,22 @@ function parseSemesterMd(content) {
115
138
  */
116
139
  function detectSemesterContext() {
117
140
  for (const searchPath of getSearchPaths()) {
118
- const candidate = path.join(searchPath, SEMESTER_FILE);
141
+ const found = findSemesterFile(searchPath);
142
+ if (!found)
143
+ continue;
119
144
  try {
120
- if (fs.existsSync(candidate)) {
121
- const content = fs.readFileSync(candidate, "utf-8");
122
- const context = parseSemesterMd(content);
123
- return {
124
- found: true,
125
- source: candidate,
126
- context,
127
- };
128
- }
145
+ const content = fs.readFileSync(found.filePath, "utf-8");
146
+ const context = parseSemesterMd(content);
147
+ return {
148
+ found: true,
149
+ source: found.filePath,
150
+ context,
151
+ ...(found.wrongCasing && {
152
+ wrongCasing: path.basename(found.filePath),
153
+ }),
154
+ };
129
155
  }
130
- catch (err) {
131
- // Permission errors or broken symlinks — skip silently
156
+ catch {
132
157
  continue;
133
158
  }
134
159
  }
@@ -146,7 +171,7 @@ function generateGreeting(result) {
146
171
  "",
147
172
  "I don't see a `SEMESTER.md` in your workspace yet. Want to set one up so I can stay context-aware of your courses and deadlines?",
148
173
  "",
149
- "Run `/semester-init` to get started, or just tell me what you're working on!",
174
+ "Run `/skill:semester --init` to get started, or just tell me what you're working on!",
150
175
  ].join("\n");
151
176
  }
152
177
  const ctx = result.context;
@@ -164,7 +189,7 @@ function generateGreeting(result) {
164
189
  courseList,
165
190
  projectLine,
166
191
  "",
167
- "What are we tackling today? `/homework`, `/project`, `/review`, `/explain`, `/leetcode`, `/exam`, or `/research`",
192
+ "What are we tackling today? `/skill:homework`, `/skill:project`, `/skill:review`, `/skill:explain`, `/skill:leetcode`, `/skill:exam`, or `/skill:research`",
168
193
  ]
169
194
  .filter((l) => l !== undefined)
170
195
  .join("\n");
@@ -220,6 +245,55 @@ function removeLegacyGlobalPersona(persona) {
220
245
  return false;
221
246
  }
222
247
  }
248
+ // ─── Welcome Tour ──────────────────────────────────────────────────────────
249
+ const WELCOME_MARKER = path.join(os.homedir(), ".pi-cs", "welcomed");
250
+ function hasBeenWelcomed() {
251
+ try {
252
+ return fs.existsSync(WELCOME_MARKER);
253
+ }
254
+ catch {
255
+ return false;
256
+ }
257
+ }
258
+ function markWelcomed() {
259
+ try {
260
+ fs.mkdirSync(path.dirname(WELCOME_MARKER), { recursive: true });
261
+ fs.writeFileSync(WELCOME_MARKER, "");
262
+ }
263
+ catch {
264
+ // Non-fatal — skip silently
265
+ }
266
+ }
267
+ function buildWelcomeTour(ctx) {
268
+ const semLine = `${ctx.semester} ${ctx.year}${ctx.week ? `, Week ${ctx.week}` : ""}`;
269
+ const courseList = ctx.courses.length
270
+ ? ctx.courses.map((c) => c.code).join(", ")
271
+ : "no courses listed yet";
272
+ return [
273
+ "🐠 Welcome to Pisces!",
274
+ "════════════════════════════════════════",
275
+ "",
276
+ `You're set up for ${semLine} — ${courseList}.`,
277
+ "",
278
+ "Skills:",
279
+ " /skill:homework Guided help — hints, not solutions, for graded work",
280
+ " /skill:explain Learn any CS concept from first principles",
281
+ " /skill:project Scaffold a new project end to end",
282
+ " /skill:review TA-level code review with severity ratings",
283
+ " /skill:leetcode Interview prep — full solutions always provided",
284
+ " /skill:exam Quizzes, mind maps, and revision plans",
285
+ " /skill:research Paper summaries and literature surveys",
286
+ " /skill:semester Update your semester context",
287
+ "",
288
+ "Quick tips:",
289
+ " • /skill:semester --update each week to track your progress",
290
+ " • /skill:semester --status to see what Pisces detects",
291
+ " • /pisces --doctor if anything seems off",
292
+ "",
293
+ "This message appears once. Type any skill to get started.",
294
+ "════════════════════════════════════════",
295
+ ].join("\n");
296
+ }
223
297
  // ─── Pi Extension Factory ──────────────────────────────────────────────────
224
298
  function default_1(pi) {
225
299
  const persona = loadPersona();
@@ -231,13 +305,22 @@ function default_1(pi) {
231
305
  if (!(0, workspace_state_1.getWorkspaceState)().isActive)
232
306
  return;
233
307
  const result = detectSemesterContext();
308
+ if (result.wrongCasing) {
309
+ ctx.ui.notify(`🐠 Pisces: found '${result.wrongCasing}' — rename it to SEMESTER.md for reliable detection on all platforms.`, "warning");
310
+ }
234
311
  if (result.found && result.context) {
235
312
  semesterCtx = result.context;
236
313
  const { semester, year, week } = result.context;
237
- ctx.ui.notify(`🐠 Pisces loaded — ${semester} ${year}${week ? `, Week ${week}` : ""}`, "info");
314
+ if (!hasBeenWelcomed()) {
315
+ ctx.ui.notify(buildWelcomeTour(result.context), "info");
316
+ markWelcomed();
317
+ }
318
+ else {
319
+ ctx.ui.notify(`🐠 Pisces loaded — ${semester} ${year}${week ? `, Week ${week}` : ""}`, "info");
320
+ }
238
321
  }
239
322
  else {
240
- ctx.ui.notify("🐠 Pisces loaded — no SEMESTER.md found. Create one to enable context awareness.", "info");
323
+ ctx.ui.notify("🐠 Pisces loaded — no SEMESTER.md found. Run /skill:semester --init to enable context awareness.", "info");
241
324
  }
242
325
  });
243
326
  pi.on("before_agent_start", async (event) => {
package/index.d.ts CHANGED
@@ -16,38 +16,9 @@
16
16
  */
17
17
  import { SemesterContext } from "./extensions/semester-detector";
18
18
  import { FolderDetectionResult } from "./extensions/folder-detector";
19
- export interface PiCsConfig {
20
- student?: {
21
- name?: string;
22
- year_of_study?: number;
23
- primary_language?: string;
24
- timezone?: string;
25
- };
26
- explanations?: {
27
- default_depth?: "beginner" | "intermediate" | "advanced";
28
- prefer_visuals?: boolean;
29
- use_analogies?: boolean;
30
- };
31
- code?: {
32
- style?: "verbose" | "balanced" | "concise";
33
- always_include_complexity?: boolean;
34
- always_include_tests?: boolean;
35
- };
36
- integrity?: {
37
- enabled?: boolean;
38
- strictness?: "strict" | "balanced" | "relaxed";
39
- };
40
- productivity?: {
41
- burnout_nudges?: boolean;
42
- session_warning_minutes?: number;
43
- weekly_summary?: boolean;
44
- };
45
- model?: {
46
- default?: string;
47
- code_heavy?: string;
48
- quick?: string;
49
- };
50
- }
19
+ export type { PiCsConfig } from "./extensions/lib/config";
20
+ export { loadConfig, deepMerge, getConfigSearchPaths } from "./extensions/lib/config";
21
+ import type { PiCsConfig } from "./extensions/lib/config";
51
22
  export interface SessionState {
52
23
  startTime: Date;
53
24
  skillsUsed: string[];
@@ -78,11 +49,6 @@ export declare const PACKAGE_NAME = "pi-cs";
78
49
  export declare const PACKAGE_VERSION = "0.1.0";
79
50
  export declare const SKILLS: readonly ["homework", "project", "review", "explain", "leetcode", "exam", "research"];
80
51
  export type SkillName = (typeof SKILLS)[number];
81
- /**
82
- * Loads and merges user config over package defaults.
83
- * Never throws — falls back to defaults on any read/parse error.
84
- */
85
- export declare function loadConfig(): PiCsConfig;
86
52
  /**
87
53
  * Creates a fresh session state object. Called by Pi at session start.
88
54
  */
package/index.js CHANGED
@@ -49,8 +49,7 @@ var __importStar = (this && this.__importStar) || (function () {
49
49
  };
50
50
  })();
51
51
  Object.defineProperty(exports, "__esModule", { value: true });
52
- exports.SKILLS = exports.PACKAGE_VERSION = exports.PACKAGE_NAME = void 0;
53
- exports.loadConfig = loadConfig;
52
+ exports.SKILLS = exports.PACKAGE_VERSION = exports.PACKAGE_NAME = exports.getConfigSearchPaths = exports.deepMerge = exports.loadConfig = void 0;
54
53
  exports.createSessionState = createSessionState;
55
54
  exports.onLoad = onLoad;
56
55
  exports.onStartup = onStartup;
@@ -62,14 +61,17 @@ exports.getActiveSemesterContext = getActiveSemesterContext;
62
61
  exports.getActiveProjectContext = getActiveProjectContext;
63
62
  exports.isValidSkill = isValidSkill;
64
63
  exports.describe = describe;
65
- const path = __importStar(require("path"));
66
64
  const fs = __importStar(require("fs"));
67
- const os = __importStar(require("os"));
68
65
  // ─── Extension Imports ─────────────────────────────────────────────────────
69
66
  const semester_detector_1 = require("./extensions/semester-detector");
70
67
  const folder_detector_1 = require("./extensions/folder-detector");
71
68
  const integrity_guard_1 = require("./extensions/integrity-guard");
72
69
  const progress_tracker_1 = require("./extensions/progress-tracker");
70
+ var config_1 = require("./extensions/lib/config");
71
+ Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_1.loadConfig; } });
72
+ Object.defineProperty(exports, "deepMerge", { enumerable: true, get: function () { return config_1.deepMerge; } });
73
+ Object.defineProperty(exports, "getConfigSearchPaths", { enumerable: true, get: function () { return config_1.getConfigSearchPaths; } });
74
+ const config_2 = require("./extensions/lib/config");
73
75
  // ─── Package Metadata ──────────────────────────────────────────────────────
74
76
  exports.PACKAGE_NAME = "pi-cs";
75
77
  exports.PACKAGE_VERSION = "0.1.0";
@@ -82,70 +84,7 @@ exports.SKILLS = [
82
84
  "exam",
83
85
  "research",
84
86
  ];
85
- // ─── Config Loader ─────────────────────────────────────────────────────────
86
- const DEFAULTS_PATH = path.join(__dirname, "../config/defaults.json");
87
- function getConfigSearchPaths() {
88
- return [
89
- path.join(process.cwd(), ".pi-cs.json"),
90
- path.join(os.homedir(), ".pi", "pi-cs.json"),
91
- path.join(os.homedir(), ".config", "pi-cs", "config.json"),
92
- ];
93
- }
94
- /**
95
- * Loads and merges user config over package defaults.
96
- * Never throws — falls back to defaults on any read/parse error.
97
- */
98
- function loadConfig() {
99
- let defaults = {};
100
- try {
101
- const raw = fs.readFileSync(DEFAULTS_PATH, "utf-8");
102
- defaults = JSON.parse(raw);
103
- }
104
- catch {
105
- // Defaults file unreadable — use hardcoded fallback
106
- defaults = {
107
- explanations: { default_depth: "intermediate", prefer_visuals: true, use_analogies: true },
108
- code: { style: "balanced", always_include_complexity: true, always_include_tests: false },
109
- integrity: { enabled: true, strictness: "balanced" },
110
- productivity: { burnout_nudges: true, session_warning_minutes: 180, weekly_summary: true },
111
- };
112
- }
113
- // Search for user config file — use first one found
114
- for (const configPath of getConfigSearchPaths()) {
115
- try {
116
- if (fs.existsSync(configPath)) {
117
- const raw = fs.readFileSync(configPath, "utf-8");
118
- const userConfig = JSON.parse(raw);
119
- return deepMerge(defaults, userConfig);
120
- }
121
- }
122
- catch {
123
- continue;
124
- }
125
- }
126
- return defaults;
127
- }
128
- /**
129
- * Deep merges two config objects. User values override defaults at every level.
130
- */
131
- function deepMerge(base, override) {
132
- const result = { ...base };
133
- for (const key of Object.keys(override)) {
134
- const baseVal = base[key];
135
- const overrideVal = override[key];
136
- if (overrideVal !== null &&
137
- typeof overrideVal === "object" &&
138
- !Array.isArray(overrideVal) &&
139
- typeof baseVal === "object" &&
140
- baseVal !== null) {
141
- result[key] = deepMerge(baseVal, overrideVal);
142
- }
143
- else if (overrideVal !== undefined) {
144
- result[key] = overrideVal;
145
- }
146
- }
147
- return result;
148
- }
87
+ // loadConfig and PiCsConfig are re-exported above from ./extensions/lib/config
149
88
  // ─── Session State ─────────────────────────────────────────────────────────
150
89
  /**
151
90
  * Creates a fresh session state object. Called by Pi at session start.
@@ -174,8 +113,8 @@ function onLoad() {
174
113
  message: `pi-cs requires Node.js >= 18. Current: ${nodeVersion}. Please upgrade.`,
175
114
  };
176
115
  }
177
- const config = loadConfig();
178
- const configSource = getConfigSearchPaths().find((p) => {
116
+ const config = (0, config_2.loadConfig)();
117
+ const configSource = (0, config_2.getConfigSearchPaths)().find((p) => {
179
118
  try {
180
119
  return fs.existsSync(p);
181
120
  }
@@ -193,7 +132,7 @@ function onLoad() {
193
132
  * Runs semester + folder detectors, builds greeting and context injection.
194
133
  */
195
134
  function onStartup(state) {
196
- const config = loadConfig();
135
+ const config = (0, config_2.loadConfig)();
197
136
  // Run semester detector
198
137
  const semesterResult = (0, semester_detector_1.run)();
199
138
  if (semesterResult) {
@@ -251,7 +190,7 @@ function onDirectoryChange(state) {
251
190
  */
252
191
  function onSkillCall(context) {
253
192
  const { skillName, userInput, sessionState } = context;
254
- const config = loadConfig();
193
+ const config = (0, config_2.loadConfig)();
255
194
  // Record skill usage
256
195
  if (!sessionState.skillsUsed.includes(skillName)) {
257
196
  sessionState.skillsUsed.push(skillName);
@@ -271,7 +210,7 @@ function onSkillCall(context) {
271
210
  * Records session stats, surfaces a burnout nudge and weekly summary if configured.
272
211
  */
273
212
  function onSessionEnd(state) {
274
- const config = loadConfig();
213
+ const config = (0, config_2.loadConfig)();
275
214
  const durationMinutes = Math.round((Date.now() - state.startTime.getTime()) / 60000);
276
215
  const trackerResult = (0, progress_tracker_1.run)({
277
216
  sessionDurationMinutes: durationMinutes,
@@ -288,7 +227,7 @@ function onSessionEnd(state) {
288
227
  * Issues a burnout warning if the session has exceeded the configured threshold.
289
228
  */
290
229
  function onMidSession(state) {
291
- const config = loadConfig();
230
+ const config = (0, config_2.loadConfig)();
292
231
  const warnAt = config.productivity?.session_warning_minutes ?? 180;
293
232
  if (!config.productivity?.burnout_nudges)
294
233
  return { warning: null };
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwFH,gDASC;AAQD,wBAsBC;AAMD,8BAkCC;AAMD,8CA6BC;AAMD,kCAoBC;AAMD,oCAgBC;AAMD,oCAmBC;AA2DD,4DAIC;AAKD,0DAIC;AAKD,oCAEC;AAMD,4BAWC;AAjXD,uCAAyB;AAEzB,8EAA8E;AAE9E,sEAIwC;AAExC,kEAIsC;AAEtC,kEAEsC;AAEtC,oEAEuC;AAKvC,kDAAsF;AAA7E,oGAAA,UAAU,OAAA;AAAE,mGAAA,SAAS,OAAA;AAAE,8GAAA,oBAAoB,OAAA;AAGpD,oDAA2E;AAiC3E,8EAA8E;AAEjE,QAAA,YAAY,GAAG,OAAO,CAAC;AACvB,QAAA,eAAe,GAAG,OAAO,CAAC;AAC1B,QAAA,MAAM,GAAG;IACpB,UAAU;IACV,SAAS;IACT,QAAQ;IACR,SAAS;IACT,UAAU;IACV,MAAM;IACN,UAAU;CACF,CAAC;AAIX,+EAA+E;AAE/E,8EAA8E;AAE9E;;GAEG;AACH,SAAgB,kBAAkB;IAChC,OAAO;QACL,SAAS,EAAE,IAAI,IAAI,EAAE;QACrB,UAAU,EAAE,EAAE;QACd,YAAY,EAAE,EAAE;QAChB,eAAe,EAAE,IAAI;QACrB,aAAa,EAAE,IAAI;QACnB,eAAe,EAAE,IAAI;KACtB,CAAC;AACJ,CAAC;AAED,+EAA+E;AAE/E;;;GAGG;AACH,SAAgB,MAAM;IACpB,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IACpC,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAE/D,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;QACf,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,0CAA0C,WAAW,mBAAmB;SAClF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAC5B,MAAM,YAAY,GAAG,IAAA,6BAAoB,GAAE,CAAC,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE;QAC7D,IAAI,CAAC;YAAC,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,KAAK,CAAC;QAAC,CAAC;IAC1D,CAAC,CAAC,IAAI,UAAU,CAAC;IAEjB,OAAO;QACL,EAAE,EAAE,IAAI;QACR,OAAO,EAAE,SAAS,uBAAe,oBAAoB,YAAY,sBAC/D,MAAM,CAAC,SAAS,EAAE,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAC/C,GAAG;KACJ,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAgB,SAAS,CAAC,KAAmB;IAC3C,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAE5B,wBAAwB;IACxB,MAAM,cAAc,GAAG,IAAA,uBAAmB,GAAE,CAAC;IAC7C,IAAI,cAAc,EAAE,CAAC;QACnB,+EAA+E;QAC/E,MAAM,SAAS,GAAG,IAAA,yCAAqB,GAAE,CAAC;QAC1C,KAAK,CAAC,eAAe,GAAG,SAAS,CAAC,OAAO,IAAI,IAAI,CAAC;IACpD,CAAC;IAED,sBAAsB;IACtB,MAAM,YAAY,GAAG,IAAA,qBAAiB,GAAE,CAAC;IACzC,KAAK,CAAC,aAAa,GAAG,IAAA,qCAAmB,GAAE,CAAC;IAE5C,6CAA6C;IAC7C,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,IAAI,cAAc,CAAC,MAAM,EAAE,CAAC;QAC1B,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,YAAY,CAAC,MAAM,EAAE,CAAC;QACxB,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAED,8BAA8B;IAC9B,YAAY,CAAC,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC;IAEhD,OAAO;QACL,gBAAgB,EAAE,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;QAC3D,QAAQ,EAAE,cAAc,CAAC,QAAQ;QACjC,MAAM;KACP,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,KAAmB;IAInD,MAAM,cAAc,GAAG,IAAA,uBAAmB,GAAE,CAAC;IAC7C,MAAM,YAAY,GAAG,IAAA,qBAAiB,GAAE,CAAC;IACzC,MAAM,gBAAgB,GAAG,IAAA,qCAAmB,GAAE,CAAC;IAE/C,MAAM,eAAe,GAAG,KAAK,CAAC,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;IAC3D,MAAM,UAAU,GAAG,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC;IAEnD,uBAAuB;IACvB,KAAK,CAAC,aAAa,GAAG,gBAAgB,CAAC;IACvC,IAAI,IAAA,yCAAqB,GAAE,CAAC,OAAO,EAAE,CAAC;QACpC,KAAK,CAAC,eAAe,GAAG,IAAA,yCAAqB,GAAE,CAAC,OAAO,IAAI,IAAI,CAAC;IAClE,CAAC;IAED,MAAM,YAAY,GAAG,CAAC,cAAc,CAAC,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAElF,iDAAiD;IACjD,MAAM,OAAO,GACX,UAAU,IAAI,UAAU,KAAK,eAAe;QAC1C,CAAC,CAAC,6BAA6B,UAAU,OAAO,gBAAgB,CAAC,OAAO,EAAE,IAAI,IAAI,SAAS,GAAG;QAC9F,CAAC,CAAC,IAAI,CAAC;IAEX,OAAO;QACL,gBAAgB,EAAE,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;QAC3C,OAAO;KACR,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,OAAyB;IACnD,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IACvD,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAE5B,qBAAqB;IACrB,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACjD,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,6CAA6C;IAC7C,IAAI,MAAM,CAAC,SAAS,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC;QACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC1C,CAAC;IAED,MAAM,WAAW,GAAG,IAAA,qBAAiB,EAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;IAEhE,OAAO;QACL,OAAO,EAAE,IAAI,EAAE,gDAAgD;QAC/D,SAAS,EAAE,WAAW,CAAC,MAAM;KAC9B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,KAAmB;IAC9C,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAC5B,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAChC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,KAAM,CAClD,CAAC;IAEF,MAAM,aAAa,GAAG,IAAA,sBAAkB,EAAC;QACvC,sBAAsB,EAAE,eAAe;QACvC,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,YAAY,EAAE,KAAK,CAAC,YAAY;KACjC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,YAAY,EAAE,cAAc,KAAK,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;QACjF,OAAO,EAAE,MAAM,CAAC,YAAY,EAAE,cAAc,KAAK,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI;KAC5F,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,KAAmB;IAC9C,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,EAAE,uBAAuB,IAAI,GAAG,CAAC;IAEnE,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,cAAc;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAEnE,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAC/B,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,KAAM,CAClD,CAAC;IAEF,0CAA0C;IAC1C,IAAI,cAAc,IAAI,MAAM,IAAI,KAAK,CAAC,eAAe,KAAK,IAAI,EAAE,CAAC;QAC/D,KAAK,CAAC,eAAe,GAAG,cAAc,CAAC;QACvC,OAAO;YACL,OAAO,EAAE,8BAA8B,cAAc,+DAA+D;SACrH,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED,8EAA8E;AAE9E;;;GAGG;AACH,SAAS,oBAAoB,CAAC,MAAkB;IAC9C,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,MAAM,CAAC,OAAO,EAAE,aAAa,EAAE,CAAC;QAClC,MAAM,UAAU,GAA2B;YACzC,CAAC,EAAE,qBAAqB;YACxB,CAAC,EAAE,sBAAsB;YACzB,CAAC,EAAE,mBAAmB;YACtB,CAAC,EAAE,mBAAmB;YACtB,CAAC,EAAE,qBAAqB;YACxB,CAAC,EAAE,sBAAsB;SAC1B,CAAC;QACF,KAAK,CAAC,IAAI,CACR,kBAAkB,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,CAC7F,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,qBAAqB,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,MAAM,CAAC,YAAY,EAAE,aAAa,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,iCAAiC,MAAM,CAAC,YAAY,CAAC,aAAa,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,EAAE,yBAAyB,EAAE,CAAC;QAC3C,KAAK,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,EAAE,oBAAoB,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAE7B,OAAO;QACL,kCAAkC;QAClC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;KACrC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,8EAA8E;AAE9E;;;GAGG;AACH,SAAgB,wBAAwB,CACtC,KAAmB;IAEnB,OAAO,KAAK,CAAC,eAAe,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,SAAgB,uBAAuB,CACrC,KAAmB;IAEnB,OAAO,KAAK,CAAC,aAAa,EAAE,OAAO,IAAI,IAAI,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,SAAgB,YAAY,CAAC,IAAY;IACvC,OAAQ,cAA4B,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,SAAgB,QAAQ;IACtB,OAAO;QACL,sBAAsB,uBAAe,EAAE;QACvC,+CAA+C;QAC/C,EAAE;QACF,mBAAmB,cAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QACvC,sFAAsF;QACtF,iCAAiC;QACjC,EAAE;QACF,yEAAyE;KAC1E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aethrekh/pi-cs",
3
- "version": "0.2.0-beta.1",
3
+ "version": "0.3.0-beta.0",
4
4
  "description": "Pisces — The CS Student Edition for Pi Coding Agent. Your personal AI teaching assistant from homework to thesis.",
5
5
  "keywords": [
6
6
  "academic",
package/pi-cs.meta.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-cs",
3
- "version": "0.2.0-beta.1",
4
- "built_at": "2026-06-06T09:28:24.401Z",
3
+ "version": "0.3.0-beta.0",
4
+ "built_at": "2026-06-08T08:53:08.015Z",
5
5
  "entry": "index.js",
6
6
  "node_minimum": "18.0.0"
7
7
  }
package/pi-package.yaml CHANGED
@@ -1,5 +1,5 @@
1
1
  name: pi-cs
2
- version: 0.2.0-beta.1
2
+ version: 0.3.0-beta.0
3
3
  display_name: "pi-cs (Pisces)"
4
4
  description: "The CS Student Edition for Pi — Your personal AI teaching assistant for Computer Science, from homework to thesis."
5
5
  icon: public/pisces-logo-512.png
@@ -41,9 +41,10 @@ skills:
41
41
  extensions:
42
42
  - name: gatekeeper
43
43
  path: extensions/gatekeeper.js
44
- description: "Gates all Pisces resources behind .pisces workspace marker; manages /pisces command"
44
+ description: "Gates all Pisces resources behind .pisces workspace marker; manages /pisces --activate | --deactivate | --status | --setup | --doctor"
45
45
  triggers:
46
46
  - resources_discover
47
+ - session_start
47
48
  - name: semester-detector
48
49
  path: extensions/semester-detector.js
49
50
  description: "Auto-detects semester context from SEMESTER.md and folder structures"