@pitcher/cli-scripts 2.20.0 → 2.21.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/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  > Pitcher CLI Tools provide a suite of command-line utilities to streamline your workflow. For the best experience, we recommend installing the tools globally using `npm install -g @pitcher/cli-scripts`.
4
4
 
5
+ Releases are published to npm automatically: a merge to `main` that bumps `version` in `package.json` publishes that version, and a merge without a bump publishes nothing (`.github/workflows/cli-scripts-publish.yml`; release flow in `CLAUDE.md`).
6
+
5
7
  ## Help
6
8
 
7
9
  ```bash
@@ -223,15 +225,12 @@ p canvases get 01CANVASID... --api-key myapikey
223
225
 
224
226
  ## To run the `shared-links` command:
225
227
 
226
- Create and read share links for canvases or files (returns the `short_url`).
228
+ Create and read share links for canvases (returns the `short_url`).
227
229
 
228
230
  ```bash
229
231
  # Create a share link for a canvas
230
232
  p shared-links create --canvas-id 01CANVASID... --api-key myapikey
231
233
 
232
- # Create a share link for a file
233
- p shared-links create --file-id 01FILEID... --api-key myapikey
234
-
235
234
  # Fetch the existing share link for a canvas
236
235
  p shared-links get --canvas-id 01CANVASID... --api-key myapikey
237
236
  ```
@@ -7,10 +7,8 @@ export interface SharedLinksDeps {
7
7
  }
8
8
  export declare function runSharedLinksCreate(opts: CommonOpts & {
9
9
  canvasId?: string;
10
- fileId?: string;
11
10
  }, deps?: SharedLinksDeps): Promise<Envelope<unknown>>;
12
11
  export declare function runSharedLinksGet(opts: CommonOpts & {
13
12
  canvasId?: string;
14
- fileId?: string;
15
13
  }, deps?: SharedLinksDeps): Promise<Envelope<unknown>>;
16
14
  export declare function createSharedLinksCommand(): Command;
@@ -4,22 +4,17 @@ import { ok, fail } from '../core/envelope.js';
4
4
  import { createCliError } from '../core/errors.js';
5
5
  import { emitEnvelope } from '../core/output.js';
6
6
  import { withAuthFlags } from './withAuth.js';
7
- /** Enforce exactly one of --canvas-id / --file-id. Scope is derived from the target. */
8
- function requireOneTarget(opts) {
9
- if (opts.canvasId && opts.fileId)
10
- throw createCliError('VALIDATION', 'pass exactly one of --canvas-id or --file-id');
11
- if (opts.canvasId)
12
- return 'canvas';
13
- if (opts.fileId)
14
- return 'file';
15
- throw createCliError('VALIDATION', 'pass exactly one of --canvas-id or --file-id');
7
+ /** Only canvases can be shared. */
8
+ function requireCanvasId(opts) {
9
+ if (!opts.canvasId)
10
+ throw createCliError('VALIDATION', '--canvas-id is required');
11
+ return opts.canvasId;
16
12
  }
17
13
  export async function runSharedLinksCreate(opts, deps = {}) {
18
14
  try {
19
- const target = requireOneTarget(opts);
15
+ const canvasId = requireCanvasId(opts);
20
16
  const client = clientFor(opts, deps);
21
- const body = target === 'canvas' ? { canvas_id: opts.canvasId } : { file_id: opts.fileId };
22
- const res = await client.post('/api/v1/shared-links/', body);
17
+ const res = await client.post('/api/v1/shared-links/', { canvas_id: canvasId });
23
18
  return ok(res.data);
24
19
  }
25
20
  catch (e) {
@@ -28,13 +23,10 @@ export async function runSharedLinksCreate(opts, deps = {}) {
28
23
  }
29
24
  export async function runSharedLinksGet(opts, deps = {}) {
30
25
  try {
31
- const target = requireOneTarget(opts);
26
+ const canvasId = requireCanvasId(opts);
32
27
  const client = clientFor(opts, deps);
33
28
  // encodeURIComponent so an id containing `/` or `..` can't retarget the request path
34
- const path = target === 'canvas'
35
- ? `/api/v1/shared-links/canvas/${encodeURIComponent(opts.canvasId)}`
36
- : `/api/v1/shared-links/file/${encodeURIComponent(opts.fileId)}`;
37
- const res = await client.get(path);
29
+ const res = await client.get(`/api/v1/shared-links/canvas/${encodeURIComponent(canvasId)}`);
38
30
  return ok(res.data);
39
31
  }
40
32
  catch (e) {
@@ -42,17 +34,15 @@ export async function runSharedLinksGet(opts, deps = {}) {
42
34
  }
43
35
  }
44
36
  export function createSharedLinksCommand() {
45
- const cmd = new Command('shared-links').description('create and read share links for canvases and files');
37
+ const cmd = new Command('shared-links').description('create and read share links for canvases');
46
38
  withAuthFlags(cmd.command('create'))
47
39
  .option('--canvas-id <id>', 'Canvas to share')
48
- .option('--file-id <id>', 'File to share')
49
40
  .action(async (options, command) => {
50
41
  const globals = command.optsWithGlobals();
51
42
  emitEnvelope(await runSharedLinksCreate({ ...options, quiet: globals.quiet }), globals);
52
43
  });
53
44
  withAuthFlags(cmd.command('get'))
54
45
  .option('--canvas-id <id>', 'Canvas whose link to fetch')
55
- .option('--file-id <id>', 'File whose link to fetch')
56
46
  .action(async (options, command) => {
57
47
  const globals = command.optsWithGlobals();
58
48
  emitEnvelope(await runSharedLinksGet({ ...options, quiet: globals.quiet }), globals);
@@ -79,6 +79,9 @@ export const analyticsPrompts = [
79
79
  '1. Call `pitcher_analytics_viewers` with the `canvasId` to list viewers and their per-viewer engagement.',
80
80
  '2. To drill into a specific viewer, call `pitcher_analytics_sessions` with the same `canvasId` plus that viewer’s ' +
81
81
  '`userEmail` (both come from step 1) to get their raw session event timeline.',
82
+ ' A session is one `Entered` of the queried id itself (canvas or section), paired by `event_pair_id`; ' +
83
+ 'an `Entered` with an empty `event_pair_id` counts as one session on its own. ' +
84
+ 'Sections listed by `pitcher_analytics_canvases` are valid `canvasId` inputs for both tools.',
82
85
  '3. Summarize who viewed, how much they engaged, and — if you drilled in — what that viewer did. Works with a member key.',
83
86
  ].join('\n');
84
87
  return [{ role: 'user', content: { type: 'text', text } }];
@@ -1,3 +1,3 @@
1
1
  import type { ResourceDescriptor } from './types.js';
2
- export declare const ANALYTICS_CATALOG = "# Pitcher analytics catalog\n\nTen read-only verbs, each an MCP tool named `pitcher_analytics_<verb>`. Pick the verb that matches the\nquestion, then call its tool. All are read-only; org scope + auth come from the server's API key (never a\ntool input) \u2014 you only pass `instanceId` (and, where noted, `canvasId`/`userEmail`).\n\n## Key tiers\n- **org-admin key required:** org, popularity, users, files, canvases\n- **instance-admin or org-admin:** instance\n- **member key OK (access follows the key user's real role):** kpi, engagement, viewers, sessions\n\n## Time model\n- **Rollup (no window):** org, instance \u2014 lifetime/current totals.\n- **Date-range, 30-day default:** kpi (activity types need dates), engagement, popularity, `users` with\n `stats: true`, files, canvases \u2014 omit dates to get the last 30 days. (`users` without `stats` is a\n full roster with no window.)\n- **Date-range, server default window:** viewers, sessions \u2014 omit dates and the service picks the window.\n\n## Verbs\n\n| Verb (tool) | Returns | Time | Key | Example question |\n|---|---|---|---|---|\n| `org` | Organization-wide rollup totals (Django) | rollup | org-admin | \"How is the whole org doing overall?\" |\n| `instance` | Single-instance rollup totals (Django) | rollup | instance-admin | \"Totals for this one workspace?\" |\n| `kpi` | KPI metrics for an instance (members, active users, \u2026) | date-range | member | \"How many active users last month?\" |\n| `engagement` | Per-DSR/content engagement rows (capped list + summary) | date-range | member | \"Which content is being engaged with?\" |\n| `popularity` | Most-viewed canvases, ranked (view-analysis) | date-range | org-admin | \"Top 10 most-viewed decks this quarter?\" |\n| `users` | User roster, or active-user stats with `stats: true` | roster: none / stats: date-range | org-admin | \"Who are my active users this month?\" |\n| `files` | File usage analytics (views/opens/engagement per file) | date-range | org-admin | \"Which files get opened the most?\" |\n| `canvases` | Per-section canvas engagement list | date-range | org-admin | \"How are my canvas sections performing?\" |\n| `viewers` | Who viewed a specific DSR canvas (per-viewer engagement) | date-range | member | \"Who viewed this DSR, and how much?\" |\n| `sessions` | One viewer's session event timeline for a DSR canvas (raw) | date-range | member | \"What did this viewer do inside the DSR?\" |\n\n## Shaping\nRow-heavy verbs (engagement, popularity, users, files, canvases, viewers) return a capped envelope:\n`summary` + `rows` (default 20, max 100 \u2014 raise with `limit`) + `total_count`/`returned`/`has_more`,\nand a `truncated` note when more rows exist. `kpi` and `sessions` return the raw payload (a KPI scalar set\nand an uncapped event timeline, respectively) \u2014 never truncated.\n\n## DSR drill-down\nFor \"who viewed X and what did they do\": call `viewers` (with `canvasId`) to list viewers, then `sessions`\n(with the same `canvasId` + the viewer's `userEmail`) to see that viewer's event timeline.\n\n## Notes\n- `popularity` maps to the canvases view-analysis endpoint (there is no separate popularity route).\n- Results can include user identities/emails \u2014 treat as customer data and handle per policy.\n";
2
+ export declare const ANALYTICS_CATALOG = "# Pitcher analytics catalog\n\nTen read-only verbs, each an MCP tool named `pitcher_analytics_<verb>`. Pick the verb that matches the\nquestion, then call its tool. All are read-only; org scope + auth come from the server's API key (never a\ntool input) \u2014 you only pass `instanceId` (and, where noted, `canvasId`/`userEmail`).\n\n## Key tiers\n- **org-admin key required:** org, popularity, users, files, canvases\n- **instance-admin or org-admin:** instance\n- **member key OK (access follows the key user's real role):** kpi, engagement, viewers, sessions\n\n## Time model\n- **Rollup (no window):** org, instance \u2014 lifetime/current totals.\n- **Date-range, 30-day default:** kpi (activity types need dates), engagement, popularity, `users` with\n `stats: true`, files, canvases \u2014 omit dates to get the last 30 days. (`users` without `stats` is a\n full roster with no window.)\n- **Date-range, server default window:** viewers, sessions \u2014 omit dates and the service picks the window.\n\n## Verbs\n\n| Verb (tool) | Returns | Time | Key | Example question |\n|---|---|---|---|---|\n| `org` | Organization-wide rollup totals (Django) | rollup | org-admin | \"How is the whole org doing overall?\" |\n| `instance` | Single-instance rollup totals (Django) | rollup | instance-admin | \"Totals for this one workspace?\" |\n| `kpi` | KPI metrics for an instance (members, active users, \u2026) | date-range | member | \"How many active users last month?\" |\n| `engagement` | Per-DSR/content engagement rows (capped list + summary) | date-range | member | \"Which content is being engaged with?\" |\n| `popularity` | Most-viewed canvases, ranked (view-analysis) | date-range | org-admin | \"Top 10 most-viewed decks this quarter?\" |\n| `users` | User roster, or active-user stats with `stats: true` | roster: none / stats: date-range | org-admin | \"Who are my active users this month?\" |\n| `files` | File usage analytics (views/opens/engagement per file) | date-range | org-admin | \"Which files get opened the most?\" |\n| `canvases` | Per-section canvas engagement list | date-range | org-admin | \"How are my canvas sections performing?\" |\n| `viewers` | Who viewed a specific DSR canvas (per-viewer engagement) | date-range | member | \"Who viewed this DSR, and how much?\" |\n| `sessions` | One viewer's session event timeline for a DSR canvas (raw) | date-range | member | \"What did this viewer do inside the DSR?\" |\n\n## Shaping\nRow-heavy verbs (engagement, popularity, users, files, canvases, viewers) return a capped envelope:\n`summary` + `rows` (default 20, max 100 \u2014 raise with `limit`) + `total_count`/`returned`/`has_more`,\nand a `truncated` note when more rows exist. `kpi` and `sessions` return the raw payload (a KPI scalar set\nand an uncapped event timeline, respectively) \u2014 never truncated.\n\n## DSR drill-down\nFor \"who viewed X and what did they do\": call `viewers` (with `canvasId`) to list viewers, then `sessions`\n(with the same `canvasId` + the viewer's `userEmail`) to see that viewer's event timeline.\nA session is one `Entered` of the queried id itself (a canvas or a section), paired by `event_pair_id`; an\n`Entered` with an empty `event_pair_id` counts as one session on its own.\n`session_count` is counted the same way as the overview's `total_sessions`. `canvases` lists sections, and their\nids are valid `canvasId` inputs for `viewers` and `sessions`.\n\n## Notes\n- `popularity` maps to the canvases view-analysis endpoint (there is no separate popularity route).\n- Results can include user identities/emails \u2014 treat as customer data and handle per policy.\n";
3
3
  export declare const analyticsCatalogResource: ResourceDescriptor;
@@ -45,6 +45,10 @@ and an uncapped event timeline, respectively) — never truncated.
45
45
  ## DSR drill-down
46
46
  For "who viewed X and what did they do": call \`viewers\` (with \`canvasId\`) to list viewers, then \`sessions\`
47
47
  (with the same \`canvasId\` + the viewer's \`userEmail\`) to see that viewer's event timeline.
48
+ A session is one \`Entered\` of the queried id itself (a canvas or a section), paired by \`event_pair_id\`; an
49
+ \`Entered\` with an empty \`event_pair_id\` counts as one session on its own.
50
+ \`session_count\` is counted the same way as the overview's \`total_sessions\`. \`canvases\` lists sections, and their
51
+ ids are valid \`canvasId\` inputs for \`viewers\` and \`sessions\`.
48
52
 
49
53
  ## Notes
50
54
  - \`popularity\` maps to the canvases view-analysis endpoint (there is no separate popularity route).
@@ -4,10 +4,9 @@ export const sharedLinksTools = [
4
4
  {
5
5
  name: 'pitcher_shared_links_create',
6
6
  title: 'Create a shared link',
7
- description: 'Create a share link (short_url) for a canvas or a file. Pass exactly one of canvasId or fileId. Additive.',
7
+ description: 'Create a share link (short_url) for a canvas. Only canvases can be shared. Additive.',
8
8
  inputShape: {
9
- canvasId: z.string().optional().describe('Canvas to share (exactly one of canvasId/fileId is required)'),
10
- fileId: z.string().optional().describe('File to share (exactly one of canvasId/fileId is required)'),
9
+ canvasId: z.string().describe('Canvas to share'),
11
10
  },
12
11
  run: runSharedLinksCreate,
13
12
  readOnly: false,
@@ -16,10 +15,9 @@ export const sharedLinksTools = [
16
15
  {
17
16
  name: 'pitcher_shared_links_get',
18
17
  title: 'Get a shared link',
19
- description: 'Retrieve the existing share link for a canvas or a file. Pass exactly one of canvasId or fileId.',
18
+ description: 'Retrieve the existing share link for a canvas.',
20
19
  inputShape: {
21
- canvasId: z.string().optional().describe('Canvas whose link to fetch (exactly one of canvasId/fileId is required)'),
22
- fileId: z.string().optional().describe('File whose link to fetch (exactly one of canvasId/fileId is required)'),
20
+ canvasId: z.string().describe('Canvas whose link to fetch'),
23
21
  },
24
22
  run: runSharedLinksGet,
25
23
  readOnly: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pitcher/cli-scripts",
3
- "version": "2.20.0",
3
+ "version": "2.21.0",
4
4
  "description": "The Pitcher CLI for various tasks in its ecosystem.",
5
5
  "author": "Pitcher AG",
6
6
  "license": "MIT",