@prepcli/prepcli 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bhavik Devganiya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,378 @@
1
+ # prepcli
2
+
3
+ Persistent AI collaboration layer — structured prompts, project context, and decision records for Claude, Cursor, Windsurf, and more.
4
+
5
+ ---
6
+
7
+ ## What it does
8
+
9
+ Most AI coding sessions start cold. The AI asks what your stack is. It suggests something you ruled out last week. It doesn't know about the deployment freeze. You spend the first 10 messages re-explaining context that hasn't changed.
10
+
11
+ prepcli fixes this in three layers:
12
+
13
+ **Layer 1 — Structured prompts.**
14
+ Six workflow slash commands (`/debug`, `/plan`, `/prep`, etc.) that ask targeted questions before the AI starts working. Better questions → better output on the first attempt → less back-and-forth.
15
+
16
+ **Layer 2 — Project context.**
17
+ `prepcli init` scans your codebase, stores your stack and constraints in the cloud, and silently injects them at the start of every AI session. The AI already knows your stack, your hard limits, and what was decided last week — without you saying a word.
18
+
19
+ **Layer 3 — Decision records.**
20
+ Every AI session gets recorded as a structured Markdown document: what was done, why, what was ruled out, linked to the git commit. Records live in a hidden git branch that travels with every clone. No account needed to read history. Works offline.
21
+
22
+ ---
23
+
24
+ ## Quick start
25
+
26
+ ```bash
27
+ npm install -g prepcli
28
+ prepcli install # copy workflow files to your AI tool
29
+ prepcli auth login # create free account (optional — enables cloud features)
30
+ prepcli init # scan project, push context, set up shadow branch
31
+ ```
32
+
33
+ Open your AI tool and type `/debug`, `/plan`, or any workflow command.
34
+
35
+ ---
36
+
37
+ ## Workflow commands
38
+
39
+ | Command | What it does |
40
+ |---|---|
41
+ | `/prep` | Universal session starter — ask targeted questions, build a structured prompt, execute on approval |
42
+ | `/debug` | Turn a vague bug report into a precise debugging prompt |
43
+ | `/plan` | Gather product and technical constraints, build a planning prompt |
44
+ | `/refactor` | Gather constraints for a safe refactor |
45
+ | `/review` | Gather review intent and constraints, build a focused code review |
46
+ | `/write` | Gather audience and constraints, build a precise writing prompt |
47
+
48
+ Each workflow follows the same structure:
49
+ - **STEP 0** — silently reads project context before asking anything
50
+ - **STEP 1–4** — asks one question at a time to fill critical gaps
51
+ - **STEP 5** — builds and shows a structured prompt, executes on your approval
52
+ - **FINAL STEP** — silently records what was done to the local session file
53
+
54
+ ---
55
+
56
+ ## Project context
57
+
58
+ Run once per project:
59
+
60
+ ```bash
61
+ cd your-project
62
+ prepcli init
63
+ ```
64
+
65
+ prepcli scans your codebase (reads `package.json`, lock files, config files, git remote) and asks three questions:
66
+
67
+ 1. **Hard limits** — things AI must never do (`never add console.log to production`)
68
+ 2. **Active constraints** — things that are true right now (`auth module is frozen until Dec 15`)
69
+ 3. **Conventions** — patterns AI would get wrong (`use AppError class, never throw raw strings`)
70
+
71
+ This context is pushed to the cloud and injected silently at the start of every AI session via STEP 0. The AI knows your stack, your limits, and your conventions without you repeating them.
72
+
73
+ ```bash
74
+ prepcli context # show current context
75
+ prepcli context --preview # show exactly what STEP 0 injects
76
+ prepcli context --edit # open in $EDITOR and push changes
77
+ ```
78
+
79
+ Stack detection covers: language, runtime, framework, database, auth, testing, API layer, styling, monorepo, i18n, CI, hosting, package manager.
80
+
81
+ ---
82
+
83
+ ## Decision records
84
+
85
+ After every AI session, `prepcli` records what was done, why, and what was ruled out — linked to the git commit it produced.
86
+
87
+ ### How recording works
88
+
89
+ During a session, the AI silently runs after each task:
90
+
91
+ ```bash
92
+ prepcli session add \
93
+ --workflow=debug \
94
+ --what="replaced date-fns with custom UTC offset handler" \
95
+ --why="date-fns fails silently on UTC+5:30 half-hour offsets"
96
+ ```
97
+
98
+ This writes to a local `.prepcli-session` file — no network, no auth, works offline.
99
+
100
+ When you run `git push`, a pre-push hook fires:
101
+
102
+ ```
103
+ [prepcli] 2 AI turns this session:
104
+ [debug] identified date-fns as root cause
105
+ [debug] replaced with custom UTC offset handler
106
+
107
+ Final summary? (Enter to skip): _
108
+ ```
109
+
110
+ Type a one-line summary and press Enter. The push is never blocked — Enter skips recording entirely.
111
+
112
+ ### Where records live
113
+
114
+ Records are stored in two places:
115
+
116
+ | | Shadow Branch | Database |
117
+ |---|---|---|
118
+ | **Content** | Full Markdown — all turns, alternatives, constraints | Lean JSON — summary, key files, turn count |
119
+ | **Needs account** | No | Yes |
120
+ | **Works offline** | Yes | No |
121
+ | **Travels with clone** | Yes | No |
122
+ | **AI reads at STEP 0** | No | Yes — feeds `recent_decisions` |
123
+
124
+ The shadow branch (`prepcli/shadow/v1`) is an orphan git branch — completely separate from your code history. It travels with every `git clone` and every `git fetch`.
125
+
126
+ ### Reading records
127
+
128
+ ```bash
129
+ prepcli log # last 10 decisions
130
+ prepcli log --workflow debug # debug sessions only
131
+ prepcli log --file src/auth.js # decisions touching this file
132
+ prepcli log --commit abc123 # specific commit
133
+ ```
134
+
135
+ Or directly with git — no prepcli needed:
136
+
137
+ ```bash
138
+ git show prepcli/shadow/v1:20260524-dec-a3f9c2b1.md
139
+ ```
140
+
141
+ ### How decisions get recorded
142
+
143
+ There are two ways a decision lands in `prepcli log` — by the AI automatically, or by you manually.
144
+
145
+ **Recorded by AI (automatic)**
146
+
147
+ After every AI task, the workflow silently runs:
148
+
149
+ ```bash
150
+ prepcli session add \
151
+ --workflow=debug \
152
+ --what="replaced date-fns with custom UTC offset handler" \
153
+ --why="date-fns fails silently on UTC+5:30 half-hour offsets"
154
+ ```
155
+
156
+ Turns accumulate locally in `.prepcli-session`. When you run `git push`, the pre-push hook fires:
157
+
158
+ ```
159
+ [prepcli] 2 AI turns this session:
160
+ [debug] identified date-fns as root cause
161
+ [debug] replaced with custom UTC offset handler
162
+
163
+ Final summary? (Enter to skip): rebuilt calendar with custom timezone handling
164
+ ```
165
+
166
+ Type a one-line summary and press Enter. One decision record is written to the shadow branch covering the full session.
167
+
168
+ ---
169
+
170
+ **Recorded manually**
171
+
172
+ You don't have to wait for an AI session or a push. If you discover something — a production bug, an architectural decision made in a meeting, a constraint found in docs — record it immediately.
173
+
174
+ ```bash
175
+ prepcli record
176
+ ```
177
+
178
+ ```
179
+ [prepcli] Recording decision
180
+
181
+ What did you decide or discover?
182
+ > switched Upstash to paid tier after finding silent rate limiting in prod
183
+
184
+ Why?
185
+ > free tier drops requests above 100/min silently, no error returned
186
+
187
+ What was ruled out? (Enter to skip)
188
+ > self-hosted Redis — too much infra overhead at current scale
189
+
190
+ Workflow? [manual/debug/plan/discovery] (Enter for manual)
191
+ > discovery
192
+
193
+ Writing to shadow branch... done.
194
+ ✓ Decision recorded (dec-3f9a1b2c)
195
+ View: prepcli log
196
+ ```
197
+
198
+ **Inline mode** — no prompts:
199
+
200
+ ```bash
201
+ prepcli record \
202
+ --what="switched Upstash to paid tier" \
203
+ --why="free tier silently drops requests above 100/min" \
204
+ --ruled-out="self-hosted Redis, Upstash Pro alternative plan" \
205
+ --workflow=discovery
206
+ ```
207
+
208
+ Manual records are marked `ai_turn_count: 0` so you can distinguish them from AI session records in `prepcli log`. Records are written directly to the shadow branch — no push needed.
209
+
210
+ ### Accessing records on a fresh clone
211
+
212
+ ```bash
213
+ git clone https://github.com/your/repo
214
+ git fetch origin prepcli/shadow/v1:prepcli/shadow/v1
215
+ prepcli log # full decision history available immediately
216
+ ```
217
+
218
+ No account required. The history is pure git objects.
219
+
220
+ ---
221
+
222
+ ## Installation options
223
+
224
+ **Temporary (no global install):**
225
+ ```bash
226
+ npx prepcli install
227
+ ```
228
+
229
+ **Permanent global install:**
230
+ ```bash
231
+ npm install -g prepcli
232
+ prepcli install
233
+ ```
234
+
235
+ When prompted, choose where to install:
236
+ - **Claude Code** — personal (`~/.claude/commands`) or project (`.claude/commands`)
237
+ - **Cursor** — `.cursor/prompts`
238
+ - **Windsurf** — `.windsurf`
239
+
240
+ ---
241
+
242
+ ## Auth
243
+
244
+ Login is via email OTP — no password required:
245
+
246
+ ```bash
247
+ prepcli auth login # sends a one-time code to your email
248
+ prepcli auth status # show current session and expiry
249
+ prepcli auth logout # invalidate session
250
+ ```
251
+
252
+ Auth is optional. Without it:
253
+ - Workflow commands work fully
254
+ - `prepcli init` saves context locally in `.prepclirc`
255
+ - Session recording writes to the shadow branch locally
256
+ - Cloud sync and `recent_decisions` injection are disabled
257
+
258
+ ---
259
+
260
+ ## Commands reference
261
+
262
+ ```bash
263
+ prepcli install # copy workflow files to AI tool directories
264
+ prepcli uninstall # remove workflow files
265
+
266
+ prepcli auth login # sign in with email OTP
267
+ prepcli auth logout # sign out
268
+ prepcli auth status # show current session
269
+
270
+ prepcli init # scan project, push context, set up shadow branch
271
+ prepcli context # show current project context
272
+ prepcli context --preview # show what STEP 0 injects
273
+ prepcli context --edit # edit and push context
274
+
275
+ prepcli session add # add a turn to the local session file (used by AI)
276
+ prepcli session show # inspect the current session
277
+ prepcli session clear # clear the session file
278
+
279
+ prepcli log # browse decision records
280
+ prepcli log --workflow <type> # filter by workflow
281
+ prepcli log --file <path> # filter by file
282
+ prepcli log --commit <hash> # show decision for a specific commit
283
+
284
+ prepcli record # manually save a decision (interactive)
285
+ prepcli record --what "..." --why "..." --ruled-out "..." --workflow manual
286
+ prepcli stats # show prompt quality scores and delta trends
287
+ prepcli doctor # diagnose setup issues
288
+ ```
289
+
290
+ ---
291
+
292
+ ## Files created in your project
293
+
294
+ | File | What it is | Safe to commit |
295
+ |---|---|---|
296
+ | `.prepclirc` | Project ID + git remote | Yes |
297
+ | `.git/hooks/pre-push` | Hook installed by `prepcli init` | N/A (in .git) |
298
+ | `.prepcli-session` | Local session accumulator | No (in .gitignore) |
299
+
300
+ ---
301
+
302
+ ## Requirements
303
+
304
+ - Node.js >= 18
305
+ - Git
306
+ - Claude Code, Cursor, or Windsurf
307
+
308
+ ---
309
+
310
+ ## Self-hosting
311
+
312
+ Run your own backend instead of using `api.prepcli.in`:
313
+
314
+ 1. Create a [Supabase](https://supabase.com) project
315
+ 2. Run `schema.sql` in the Supabase SQL editor
316
+ 3. Deploy `worker/` to [Cloudflare Workers](https://workers.cloudflare.com)
317
+ 4. Set Cloudflare secrets:
318
+ ```bash
319
+ wrangler secret put SUPABASE_URL
320
+ wrangler secret put SUPABASE_ANON_KEY
321
+ ```
322
+ 5. Point the CLI at your worker:
323
+ ```bash
324
+ PREPCLI_API_URL=https://your-worker.workers.dev prepcli init
325
+ ```
326
+
327
+ ---
328
+
329
+ ## Codebase structure
330
+
331
+ ```
332
+ bin/
333
+ prepcli.js CLI entry point
334
+
335
+ src/
336
+ commands/
337
+ auth.js login / logout / status
338
+ init.js project setup
339
+ context.js read and edit project context
340
+ session.js local session accumulator
341
+ hook.js pre-push hook handler
342
+ log.js browse decision records
343
+ record.js manual decision entry
344
+ install.js workflow file installer
345
+ stats.js quality scores (Phase 4)
346
+ team.js team management (Phase 6)
347
+ doctor.js setup diagnostics (Phase 6)
348
+
349
+ lib/
350
+ api.js HTTP client → api.prepcli.in
351
+ config.js auth token + .prepclirc helpers
352
+ detect.js stack auto-detection
353
+ git.js shadow branch plumbing
354
+ session-file.js .prepcli-session read/write
355
+ decision.js Markdown record builder
356
+ targets/ AI tool detection (Claude, Cursor, Windsurf)
357
+
358
+ workflows/
359
+ prep.md /prep workflow
360
+ debug.md /debug workflow
361
+ plan.md /plan workflow
362
+ refactor.md /refactor workflow
363
+ review.md /review workflow
364
+ write.md /write workflow
365
+
366
+ worker/
367
+ src/index.js Cloudflare Worker (API layer)
368
+
369
+ schema.sql Supabase schema + RLS policies
370
+ docs/
371
+ design.md Visual design reference
372
+ ```
373
+
374
+ ---
375
+
376
+ ## License
377
+
378
+ MIT
package/bin/prepcli.js ADDED
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { program } = require("commander");
5
+
6
+ program
7
+ .name("prepcli")
8
+ .description("Persistent AI collaboration layer — context, decisions, and self-improving prompts")
9
+ .version("0.1.0");
10
+
11
+ // ── Auth ──────────────────────────────────────────────────────────────────────
12
+ program
13
+ .command("auth <action>")
14
+ .description("login | logout | status")
15
+ .action((action) => require("../src/commands/auth").run(action));
16
+
17
+ // ── Init ──────────────────────────────────────────────────────────────────────
18
+ program
19
+ .command("init")
20
+ .description("Scan codebase, create .prepclirc, push initial context to cloud")
21
+ .action(() => require("../src/commands/init").run());
22
+
23
+ // ── Install ───────────────────────────────────────────────────────────────────
24
+ program
25
+ .command("install")
26
+ .description("Copy workflow files to AI tool directories")
27
+ .option("--all", "Install to all detected tools")
28
+ .option("--tool <ids>", "Comma-separated tool ids (claude-project, cursor, windsurf…)")
29
+ .option("--yes, -y", "Skip confirmation prompt")
30
+ .action((opts) => require("../src/commands/install").run(opts));
31
+
32
+ // ── Uninstall ─────────────────────────────────────────────────────────────────
33
+ program
34
+ .command("uninstall")
35
+ .description("Remove workflow files from AI tool directories")
36
+ .option("--all", "Remove from all locations without prompting")
37
+ .option("--yes, -y", "Skip confirmation prompt")
38
+ .action((opts) => require("../src/commands/uninstall").run(opts));
39
+
40
+ // ── Context ───────────────────────────────────────────────────────────────────
41
+ program
42
+ .command("context")
43
+ .description("Show current project context from cloud")
44
+ .option("--preview", "Show exactly what STEP 0 would inject")
45
+ .option("--edit", "Open context in editor")
46
+ .action((opts) => require("../src/commands/context").run(opts));
47
+
48
+ // ── Session (AI calls this silently) ─────────────────────────────────────────
49
+ program
50
+ .command("session <action>")
51
+ .description("add | show | clear — manage the local AI session accumulator")
52
+ .option("--workflow <type>", "Workflow type (debug, plan, review, prep, refactor, write)")
53
+ .option("--what <text>", "One sentence: what was done")
54
+ .option("--why <text>", "One sentence: why this approach")
55
+ .action((action, opts) => require("../src/commands/session").run(action, opts));
56
+
57
+ // ── Internal git hook handler ─────────────────────────────────────────────────
58
+ program
59
+ .command("_hook <name>", { hidden: true })
60
+ .description("Internal: called by git hooks installed by prepcli init")
61
+ .action((name) => require("../src/commands/hook").run(name));
62
+
63
+ // ── Decision log ──────────────────────────────────────────────────────────────
64
+ program
65
+ .command("log")
66
+ .description("Browse AI decision records linked to commits")
67
+ .option("--workflow <type>", "Filter by workflow type (debug, plan, review…)")
68
+ .option("--file <path>", "Filter by file path")
69
+ .option("--commit <hash>", "Show decision for a specific commit")
70
+ .option("--last <period>", "Filter by time period (e.g. 30d)")
71
+ .action((opts) => require("../src/commands/log").run(opts));
72
+
73
+ // ── Record ────────────────────────────────────────────────────────────────────
74
+ program
75
+ .command("record")
76
+ .description("Manually save a decision to the shadow branch")
77
+ .option("--what <text>", "What was decided or discovered")
78
+ .option("--why <text>", "Why this approach over alternatives")
79
+ .option("--ruled-out <text>", "What was considered and rejected (comma-separated)")
80
+ .option("--workflow <type>", "Workflow type (manual, debug, plan, discovery)")
81
+ .action((opts) => require("../src/commands/record").run(opts));
82
+
83
+ // ── Stats ─────────────────────────────────────────────────────────────────────
84
+ program
85
+ .command("stats")
86
+ .description("Show prompt quality scores and delta trends")
87
+ .option("--workflow <type>", "Filter by workflow type")
88
+ .option("--last <period>", "Filter by time period (e.g. 30d)")
89
+ .option("--compare <period>", "Compare to an earlier period")
90
+ .action((opts) => require("../src/commands/stats").run(opts));
91
+
92
+ // ── Team ──────────────────────────────────────────────────────────────────────
93
+ program
94
+ .command("team <action> [email]")
95
+ .description("invite | list | remove")
96
+ .action((action, email) => require("../src/commands/team").run(action, email));
97
+
98
+ // ── Doctor ────────────────────────────────────────────────────────────────────
99
+ program
100
+ .command("doctor")
101
+ .description("Diagnose setup issues (auth, .prepclirc, git hooks, push refspec)")
102
+ .action(() => require("../src/commands/doctor").run());
103
+
104
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@prepcli/prepcli",
3
+ "version": "0.1.0",
4
+ "description": "Persistent AI collaboration layer — context, decisions, and self-improving prompts",
5
+ "bin": {
6
+ "prepcli": "./bin/prepcli.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "src",
11
+ "workflows",
12
+ "LICENSE"
13
+ ],
14
+ "scripts": {
15
+ "test": "node --test src/tests/**/*.test.js"
16
+ },
17
+ "dependencies": {
18
+ "commander": "^12.0.0",
19
+ "dotenv": "^17.4.2"
20
+ },
21
+ "keywords": [
22
+ "ai",
23
+ "cli",
24
+ "claude",
25
+ "cursor",
26
+ "prompt",
27
+ "context",
28
+ "developer-tools",
29
+ "llm",
30
+ "workflow"
31
+ ],
32
+ "author": "Bhavik Devganiya",
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/bhavviik/prepcli.git"
37
+ },
38
+ "engines": {
39
+ "node": ">=18"
40
+ }
41
+ }
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+
3
+ const readline = require("node:readline/promises");
4
+ const api = require("../lib/api");
5
+ const { writeConfig, deleteConfig, readConfig } = require("../lib/config");
6
+
7
+ // ── Login ─────────────────────────────────────────────────────────────────────
8
+ async function login() {
9
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
10
+
11
+ try {
12
+ const email = (await rl.question("Email: ")).trim();
13
+
14
+ if (!email || !email.includes("@")) {
15
+ console.error("Enter a valid email address.");
16
+ process.exit(1);
17
+ }
18
+
19
+ try {
20
+ await api.post("/auth/otp", { email });
21
+ } catch (err) {
22
+ console.error("Failed to send code:", err.message);
23
+ process.exit(1);
24
+ }
25
+
26
+ console.log("\nCode sent to " + email);
27
+ console.log("Check your email and enter the code below.\n");
28
+
29
+ const code = (await rl.question("Enter code: ")).trim();
30
+
31
+ if (!code || !/^\d+$/.test(code)) {
32
+ console.error("Invalid code.");
33
+ process.exit(1);
34
+ }
35
+
36
+ let data;
37
+ try {
38
+ data = await api.post("/auth/verify", { email, token: code });
39
+ } catch {
40
+ console.error("Invalid or expired code. Run: prepcli auth login");
41
+ process.exit(1);
42
+ }
43
+
44
+ writeConfig({
45
+ access_token: data.access_token,
46
+ refresh_token: data.refresh_token,
47
+ user_id: data.user_id,
48
+ email: data.email,
49
+ expires_at: data.expires_at,
50
+ });
51
+
52
+ console.log("\nLogged in as " + data.email);
53
+ console.log("Run `prepcli init` inside your project to set it up.");
54
+
55
+ } finally {
56
+ rl.close();
57
+ }
58
+ }
59
+
60
+ // ── Logout ────────────────────────────────────────────────────────────────────
61
+ async function logout() {
62
+ const cfg = readConfig();
63
+
64
+ if (!cfg) {
65
+ console.log("Already logged out.");
66
+ return;
67
+ }
68
+
69
+ try {
70
+ await api.post("/auth/logout", {}, cfg.access_token);
71
+ } catch {
72
+ // Local cleanup is what matters
73
+ }
74
+
75
+ deleteConfig();
76
+ console.log("Logged out.");
77
+ }
78
+
79
+ // ── Status ────────────────────────────────────────────────────────────────────
80
+ async function status() {
81
+ const { refreshIfNeeded } = require("../lib/config");
82
+ let cfg = readConfig();
83
+
84
+ if (!cfg || !cfg.access_token) {
85
+ console.log("Not logged in.");
86
+ console.log("Run: prepcli auth login");
87
+ return;
88
+ }
89
+
90
+ const now = Math.floor(Date.now() / 1000);
91
+ const ttl = (cfg.expires_at || 0) - now;
92
+
93
+ if (ttl <= 300) {
94
+ const refreshed = await refreshIfNeeded();
95
+ if (refreshed) cfg = refreshed;
96
+ }
97
+
98
+ const expired = cfg.expires_at && now >= cfg.expires_at;
99
+ const expiresStr = cfg.expires_at
100
+ ? new Date(cfg.expires_at * 1000).toLocaleString()
101
+ : "unknown";
102
+
103
+ const tokenStatus = expired
104
+ ? "EXPIRED — run: prepcli auth login"
105
+ : "valid until " + expiresStr;
106
+
107
+ console.log("Logged in as: " + cfg.email);
108
+ console.log("User ID: " + cfg.user_id);
109
+ console.log("Token: " + tokenStatus);
110
+ console.log("Auto-refresh: enabled");
111
+ }
112
+
113
+ // ── Router ────────────────────────────────────────────────────────────────────
114
+ async function run(action) {
115
+ switch (action) {
116
+ case "login": await login(); break;
117
+ case "logout": await logout(); break;
118
+ case "status": await status(); break;
119
+ default:
120
+ console.error("Unknown auth action: \"" + action + "\". Use: login | logout | status");
121
+ process.exit(1);
122
+ }
123
+ }
124
+
125
+ module.exports = { run };