@aethrekh/pi-cs 0.2.0 → 0.3.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 +21 -0
- package/README.md +44 -41
- package/config/schema.json +11 -0
- package/extensions/gatekeeper.js +230 -33
- package/extensions/lib/config.js +113 -0
- package/extensions/semester-detector.js +100 -17
- package/index.d.ts +3 -37
- package/index.js +13 -74
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/pi-cs.meta.json +2 -2
- package/pi-package.yaml +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.3.0](https://github.com/AshishBagdane/pi-cs/compare/v0.3.0-beta.0...v0.3.0) (2026-06-08)
|
|
11
|
+
|
|
12
|
+
## [0.3.0-beta.0](https://github.com/AshishBagdane/pi-cs/compare/v0.2.0...v0.3.0-beta.0) (2026-06-08)
|
|
13
|
+
|
|
14
|
+
### ### Added
|
|
15
|
+
|
|
16
|
+
* documentation refresh and SEMESTER.md robustness (UX items 2–4) ([24a3026](https://github.com/AshishBagdane/pi-cs/commit/24a30268c33b13f2999a11e8be2a5d004d83348f))
|
|
17
|
+
* **gatekeeper:** implement UX improvements for first-time users ([7cfa3a4](https://github.com/AshishBagdane/pi-cs/commit/7cfa3a477879dacb0d6efa1ccaa1c36705690dae))
|
|
18
|
+
* **gatekeeper:** register skill stubs to redirect inactive-workspace users ([8bab072](https://github.com/AshishBagdane/pi-cs/commit/8bab072670e73fcf3d96b3ec4e3ecfb12fbbd56e))
|
|
19
|
+
* progressive disclosure, welcome tour, and custom workspace paths (UX items 5–7) ([d9af688](https://github.com/AshishBagdane/pi-cs/commit/d9af6884f18e75d0255697092c383e85cf03388d))
|
|
20
|
+
|
|
21
|
+
### ### Fixed
|
|
22
|
+
|
|
23
|
+
* **gatekeeper:** show /skill:<name> correction when workspace is active ([05d7ce6](https://github.com/AshishBagdane/pi-cs/commit/05d7ce6b82368a37decb9854ca5d71ada138425c))
|
|
24
|
+
* update all skill invocations to /skill:<name> format ([86471ed](https://github.com/AshishBagdane/pi-cs/commit/86471ed3b38974961f1b9045fd8c13c84aed9e3b))
|
|
25
|
+
|
|
26
|
+
### ### Changed
|
|
27
|
+
|
|
28
|
+
* 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)
|
|
29
|
+
* update CLAUDE.md for v0.2.0 workspace activation changes ([0fb8b85](https://github.com/AshishBagdane/pi-cs/commit/0fb8b8503e3a382344919790e3e53f6f51cb3273))
|
|
30
|
+
|
|
10
31
|
## [0.2.0](https://github.com/AshishBagdane/pi-cs/compare/v0.2.0-beta.1...v0.2.0) (2026-06-06)
|
|
11
32
|
|
|
12
33
|
### ### Changed
|
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
|
|
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)
|
package/config/schema.json
CHANGED
|
@@ -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
|
}
|
package/extensions/gatekeeper.js
CHANGED
|
@@ -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
|
|
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: []
|
|
27
|
-
//
|
|
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
|
-
// ───
|
|
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
|
-
|
|
47
|
-
|
|
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
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
83
|
-
"
|
|
84
|
-
`
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
92
|
-
"
|
|
93
|
-
|
|
94
|
-
`
|
|
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
|
-
|
|
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
|
-
// ──
|
|
102
|
-
|
|
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
|
-
|
|
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
|
|
141
|
+
const found = findSemesterFile(searchPath);
|
|
142
|
+
if (!found)
|
|
143
|
+
continue;
|
|
119
144
|
try {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
//
|
|
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":";AAAA;;;;;;;;;;;;;;;GAeG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/pi-cs.meta.json
CHANGED
package/pi-package.yaml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: pi-cs
|
|
2
|
-
version: 0.
|
|
2
|
+
version: 0.3.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
|
|
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"
|