@prave/cli 1.4.8 → 1.4.9

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.
@@ -89,14 +89,26 @@ export async function installCommand(slug, opts = {}) {
89
89
  process.exitCode = 1;
90
90
  return;
91
91
  }
92
- // Best-effort analyze pass on the freshly written files.
92
+ // Best-effort analyze pass on the freshly written files. Silently
93
+ // catching all errors here used to hide a real failure mode: when the
94
+ // user's CLI token had expired, every analyze call returned 401, the
95
+ // install ledger filled up, but skill_metadata stayed empty — so the
96
+ // web dashboard rendered honest zeros and looked broken. We still
97
+ // don't bail on transient errors (the install itself succeeded), but
98
+ // we DO surface auth failures once so the user knows to re-login.
99
+ let authWarned = false;
93
100
  for (const s of installedSlugs) {
94
101
  const path = join(CONFIG.skillsDir, s, 'SKILL.md');
95
102
  try {
96
103
  const content = await readFile(path, 'utf8');
97
104
  await api
98
105
  .post('/api/v1/intelligence/analyze', { content, file_path: path, agent_type: 'claude' }, true)
99
- .catch(() => { });
106
+ .catch((err) => {
107
+ if (!authWarned && err instanceof ApiError && err.status === 401) {
108
+ authWarned = true;
109
+ log.warn('Your CLI session expired — Skill intelligence will be out of sync until you run `prave login`.');
110
+ }
111
+ });
100
112
  }
101
113
  catch {
102
114
  /* file unreadable — skip */
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import { usageHookInstallCommand, usageHookUninstallCommand, usageReportCommand,
24
24
  import { whatdoesCommand } from './commands/whatdoes.js';
25
25
  import { whoamiCommand } from './commands/whoami.js';
26
26
  import { initAnalytics } from './lib/analytics.js';
27
- import { nudgeFirstRun } from './lib/nudge.js';
27
+ import { captureAuthSnapshot, nudgeFirstRun } from './lib/nudge.js';
28
28
  const __dirname = dirname(fileURLToPath(import.meta.url));
29
29
  const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf8'));
30
30
  initAnalytics(pkg.version);
@@ -204,6 +204,19 @@ program
204
204
  'Docs: https://prave.app/docs',
205
205
  ].join('\n'));
206
206
  });
207
+ // Capture auth state BEFORE the command runs. Critical for the
208
+ // first-run welcome banner — if the user's very first command is
209
+ // `prave login`, the postAction hook fires AFTER credentials are
210
+ // saved, so a live auth check would read "signed in" and suppress
211
+ // the welcome. The preAction snapshot freezes the pre-command state.
212
+ program.hook('preAction', async () => {
213
+ try {
214
+ await captureAuthSnapshot();
215
+ }
216
+ catch {
217
+ /* never block the command on nudge bookkeeping */
218
+ }
219
+ });
207
220
  // Global first-run banner. Fires once on the user's very first command
208
221
  // regardless of which one it was — catches commands that don't have a
209
222
  // per-action nudge wired in (e.g. `prave docs`, `prave conflicts`).
package/dist/lib/nudge.js CHANGED
@@ -4,6 +4,23 @@ import { CONFIG } from './config.js';
4
4
  import { loadCredentials } from './credentials.js';
5
5
  import { isAlwaysShow, NUDGE_FIRST_RUN, nudgeFor, } from './nudge-constants.js';
6
6
  let alreadyNudged = false;
7
+ /**
8
+ * Auth snapshot captured at command START, used at command END.
9
+ *
10
+ * Without this, `prave login` (very common first-ever command) traps
11
+ * us: the postAction hook fires AFTER credentials are saved, so the
12
+ * auth check inside canNudge() reads "signed in" → first-run banner
13
+ * suppressed → `first_run: false` written → user never sees the
14
+ * welcome message on any future command. The snapshot freezes the
15
+ * pre-command auth state so the first-run welcome still fires for a
16
+ * user whose very first command is the login itself.
17
+ */
18
+ let wasAnonymousAtStart = null;
19
+ export async function captureAuthSnapshot() {
20
+ if (wasAnonymousAtStart !== null)
21
+ return;
22
+ wasAnonymousAtStart = !(await isAuthenticated());
23
+ }
7
24
  /* --------------------------------------------------------------------- */
8
25
  /* Auth + state helpers */
9
26
  /* --------------------------------------------------------------------- */
@@ -101,11 +118,28 @@ async function canNudge() {
101
118
  * (no nudge_count yet AND first_run not yet set to false). Always strong,
102
119
  * always bypasses the throttle.
103
120
  *
121
+ * Uses the START-of-command auth snapshot, not live state. That way the
122
+ * banner still appears when the user's very first command is `prave
123
+ * login` itself — login completes, the postAction hook fires, and we
124
+ * still know they came in anonymous so the welcome is appropriate.
125
+ *
104
126
  * Returns true when shown — call sites use this to skip the regular nudge
105
127
  * on the same turn so we don't double-print.
106
128
  */
107
129
  export async function nudgeFirstRun() {
108
- if (!(await canNudge()))
130
+ if (alreadyNudged)
131
+ return false;
132
+ if (!process.stdout.isTTY)
133
+ return false;
134
+ if (process.env.PRAVE_QUIET === '1')
135
+ return false;
136
+ if (process.env.PRAVE_TELEMETRY === '0')
137
+ return false;
138
+ // Honour the auth-at-start snapshot. If the user opened the session
139
+ // already authenticated, the welcome conversion banner doesn't fit —
140
+ // they don't need a "create free account" pitch.
141
+ const startedAnon = wasAnonymousAtStart ?? !(await isAuthenticated());
142
+ if (!startedAnon)
109
143
  return false;
110
144
  const cfg = await readConfig();
111
145
  // first_run defaults to "yes, show it" unless we've already flipped
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prave/cli",
3
- "version": "1.4.8",
3
+ "version": "1.4.9",
4
4
  "description": "Prave CLI — discover, install, version, test, and ship Claude Skills. The developer platform for the complete Skill lifecycle.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -54,7 +54,7 @@
54
54
  "ora": "^8.0.1",
55
55
  "tar": "^7.4.3",
56
56
  "undici": "^6.18.0",
57
- "@prave/shared": "1.4.8"
57
+ "@prave/shared": "1.4.9"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@types/node": "^20.12.7",