@tenderprompt/cli 0.1.8 → 0.1.10

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.
Files changed (3) hide show
  1. package/README.md +4 -0
  2. package/dist/index.js +324 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -157,6 +157,8 @@ tender app analytics export catalog artifact_123 --range 30d --json
157
157
  tender app analytics export query artifact_123 --spec chart.json --format csv
158
158
 
159
159
  tender app analytics suggestions artifact_123 --range 30d --json
160
+
161
+ tender app analytics charts templates --json
160
162
  ```
161
163
 
162
164
  Safe write previews:
@@ -178,6 +180,8 @@ Agents should combine `capabilities --include-catalog` with local source-code
178
180
  inspection. For example, inspect where the app calls
179
181
  `__TP_ANALYTICS.invoke(...)`, propose a chart spec, validate it with
180
182
  `analytics query`, and only then create a saved chart.
183
+ Use `charts templates --json` for reusable chart shapes; adapt any placeholders
184
+ to observed events, paths, or promoted property keys before querying or saving.
181
185
 
182
186
  ## Safety Model
183
187
 
package/dist/index.js CHANGED
@@ -1,11 +1,38 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "node:child_process";
3
+ import { readFileSync } from "node:fs";
3
4
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
4
5
  import os from "node:os";
5
6
  import path from "node:path";
6
7
  import { fileURLToPath } from "node:url";
7
8
  import { generateTenderAppEnvTypes, validateTenderApp, } from "@tenderprompt/tender-app-validator";
8
9
  import { collectTenderAppFiles } from "./local-files.js";
10
+ const CLI_PACKAGE_NAME = "@tenderprompt/cli";
11
+ const UNKNOWN_CLI_VERSION = "0.0.0";
12
+ const RECOMMENDED_TENDER_PROMPT_SKILL = {
13
+ name: "tender-prompt",
14
+ version: "0.1.1",
15
+ source: "git@github.com:tenderprompt/skills.git",
16
+ path: "skills/tender-prompt/SKILL.md",
17
+ updateHint: "Refresh the tender-prompt skill from git@github.com:tenderprompt/skills.git when the local skill version is older.",
18
+ };
19
+ function readCliPackageVersion() {
20
+ try {
21
+ const packageJsonPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../package.json");
22
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
23
+ if (typeof parsed === "object" &&
24
+ parsed !== null &&
25
+ "version" in parsed &&
26
+ typeof parsed.version === "string") {
27
+ return parsed.version;
28
+ }
29
+ }
30
+ catch {
31
+ return UNKNOWN_CLI_VERSION;
32
+ }
33
+ return UNKNOWN_CLI_VERSION;
34
+ }
35
+ const CLI_PACKAGE_VERSION = readCliPackageVersion();
9
36
  class TenderCliUsageError extends Error {
10
37
  constructor(message) {
11
38
  super(message);
@@ -239,6 +266,7 @@ Commands:
239
266
  tender app analytics suggestions <app-id> Generate starter chart suggestions
240
267
  tender app analytics dashboards <app-id> List or create dashboards
241
268
  tender app analytics export <kind> <app-id> Export catalog metadata or query rows
269
+ tender app analytics charts templates Print reusable chart spec templates
242
270
  tender app analytics charts create <app-id> Create a saved chart
243
271
 
244
272
  Examples:
@@ -250,6 +278,7 @@ Examples:
250
278
  tender app analytics export query artifact_123 --spec chart.json --format csv
251
279
  tender app analytics suggestions artifact_123 --range 2026-04-01..2026-04-30 --json
252
280
  tender app analytics dashboards artifact_123 --create "Agent dashboard" --json
281
+ tender app analytics charts templates --json
253
282
  tender app analytics charts create artifact_123 --dashboard analytics_dashboard_123 --title "Signup funnel" --spec funnel.json --json`;
254
283
  }
255
284
  function analyticsSummaryHelp() {
@@ -313,6 +342,16 @@ Examples:
313
342
  tender app analytics dashboards artifact_123 --create "Agent dashboard" --dry-run --json
314
343
  tender app analytics dashboards artifact_123 --create "Agent dashboard" --json`;
315
344
  }
345
+ function analyticsChartsTemplatesHelp() {
346
+ return `Usage: tender app analytics charts templates [--json]
347
+
348
+ Print reusable chart spec templates for agents. Adapt templates to observed
349
+ events, paths, and property keys from analytics capabilities before querying or
350
+ saving.
351
+
352
+ Examples:
353
+ tender app analytics charts templates --json`;
354
+ }
316
355
  function analyticsChartsCreateHelp() {
317
356
  return `Usage: tender app analytics charts create <app-id> --dashboard <dashboard-id> --title <title> --spec <file|-> [--dry-run] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
318
357
 
@@ -638,9 +677,15 @@ function tenderCliCapabilities() {
638
677
  return {
639
678
  ok: true,
640
679
  surface: "tender_cli",
680
+ cli: {
681
+ name: CLI_PACKAGE_NAME,
682
+ version: CLI_PACKAGE_VERSION,
683
+ latestRunner: "npm exec --yes @tenderprompt/cli@latest --",
684
+ },
685
+ recommendedSkill: RECOMMENDED_TENDER_PROMPT_SKILL,
641
686
  description: "Agent-safe Tender App source, lifecycle, analytics, and local scaffold controls.",
642
687
  discovery: {
643
- primary: "tender capabilities --json",
688
+ primary: "npm exec --yes @tenderprompt/cli@latest -- capabilities --json",
644
689
  layeredHelp: [
645
690
  "tender --help",
646
691
  "tender auth --help",
@@ -692,6 +737,7 @@ function tenderCliCapabilities() {
692
737
  when: "When understanding app analytics or creating saved charts.",
693
738
  commands: [
694
739
  "tender app analytics capabilities <artifact-id> --include-catalog --range 30d --json",
740
+ "tender app analytics charts templates --json",
695
741
  "tender app analytics query <artifact-id> --spec chart.json --json",
696
742
  "tender app analytics export query <artifact-id> --spec chart.json --format csv",
697
743
  "tender app analytics dashboards <artifact-id> --create \"Agent dashboard\" --dry-run --json",
@@ -749,6 +795,12 @@ function tenderCliCapabilities() {
749
795
  requiresAuth: true,
750
796
  writes: false,
751
797
  },
798
+ {
799
+ command: "tender app analytics charts templates --json",
800
+ purpose: "Print reusable chart spec templates for agents to adapt to observed analytics data.",
801
+ requiresAuth: false,
802
+ writes: false,
803
+ },
752
804
  ],
753
805
  security: [
754
806
  "Prefer artifact-scoped device tokens.",
@@ -2522,6 +2574,21 @@ function parseAnalyticsDashboardsArgs(args) {
2522
2574
  }
2523
2575
  return { command: "app analytics dashboards", artifactId, name, dryRun, ...parsed };
2524
2576
  }
2577
+ function parseAnalyticsChartsTemplatesArgs(args) {
2578
+ if (args.includes("--help") || args.includes("-h")) {
2579
+ return { command: "help", topic: "app analytics charts templates" };
2580
+ }
2581
+ let json = false;
2582
+ for (let index = 0; index < args.length; index += 1) {
2583
+ const arg = args[index];
2584
+ if (arg === "--json") {
2585
+ json = true;
2586
+ continue;
2587
+ }
2588
+ throw new TenderCliUsageError(`Unknown analytics charts templates option: ${arg}`);
2589
+ }
2590
+ return { command: "app analytics charts templates", json };
2591
+ }
2525
2592
  function parseAnalyticsExportCatalogArgs(args) {
2526
2593
  if (args.includes("--help") || args.includes("-h")) {
2527
2594
  return { command: "help", topic: "app analytics export catalog" };
@@ -2599,6 +2666,244 @@ function parseAnalyticsExportQueryArgs(args) {
2599
2666
  ...parsed,
2600
2667
  };
2601
2668
  }
2669
+ function analyticsChartTemplates() {
2670
+ return [
2671
+ {
2672
+ id: "daily_custom_events",
2673
+ title: "Daily custom events",
2674
+ visualization: "line",
2675
+ purpose: "Track app-owned business events over time.",
2676
+ usableAsIs: true,
2677
+ adaptWith: [
2678
+ "Use when the catalog has observed custom events.",
2679
+ "Switch the range preset to 7d or 90d when the user's question asks for that window.",
2680
+ ],
2681
+ spec: {
2682
+ version: 1,
2683
+ dataset: "events",
2684
+ visualization: "line",
2685
+ metric: { name: "custom_event_count" },
2686
+ groupBy: [{ field: "day" }],
2687
+ filters: [],
2688
+ range: { preset: "30d" },
2689
+ sort: [{ field: "label", direction: "asc" }],
2690
+ limit: 30,
2691
+ },
2692
+ },
2693
+ {
2694
+ id: "top_custom_events",
2695
+ title: "Top custom events",
2696
+ visualization: "bar",
2697
+ purpose: "Show which app-owned events happen most often.",
2698
+ usableAsIs: true,
2699
+ adaptWith: [
2700
+ "Use this before proposing deeper charts so the agent can see the dominant event shape.",
2701
+ ],
2702
+ spec: {
2703
+ version: 1,
2704
+ dataset: "events",
2705
+ visualization: "bar",
2706
+ metric: { name: "custom_event_count" },
2707
+ groupBy: [{ field: "event_name" }],
2708
+ filters: [],
2709
+ range: { preset: "30d" },
2710
+ sort: [{ field: "value", direction: "desc" }],
2711
+ limit: 10,
2712
+ },
2713
+ },
2714
+ {
2715
+ id: "top_request_paths",
2716
+ title: "Top request paths",
2717
+ visualization: "bar",
2718
+ purpose: "Find the generated app routes receiving the most traffic.",
2719
+ usableAsIs: true,
2720
+ adaptWith: [
2721
+ "Use only when the capabilities catalog reports request paths.",
2722
+ "Inspect the local source to understand what the busiest routes represent.",
2723
+ ],
2724
+ spec: {
2725
+ version: 1,
2726
+ dataset: "events",
2727
+ visualization: "bar",
2728
+ metric: { name: "request_count" },
2729
+ groupBy: [{ field: "path" }],
2730
+ filters: [],
2731
+ range: { preset: "30d" },
2732
+ sort: [{ field: "value", direction: "desc" }],
2733
+ limit: 10,
2734
+ },
2735
+ },
2736
+ {
2737
+ id: "event_funnel_by_name",
2738
+ title: "Event funnel",
2739
+ visualization: "funnel",
2740
+ purpose: "Measure ordered conversion through observed custom event names.",
2741
+ usableAsIs: false,
2742
+ placeholders: {
2743
+ eventNames: ["<start_event>", "<middle_event>", "<conversion_event>"],
2744
+ },
2745
+ adaptWith: [
2746
+ "Replace placeholders with ordered event names from catalog classification or observed events.",
2747
+ "Use only event names observed for the selected app.",
2748
+ "Validate with analytics query before saving because funnel order changes the meaning.",
2749
+ ],
2750
+ spec: {
2751
+ version: 1,
2752
+ dataset: "events",
2753
+ visualization: "funnel",
2754
+ metric: { name: "custom_event_count" },
2755
+ groupBy: [{ field: "event_name" }],
2756
+ filters: [
2757
+ {
2758
+ field: "event_name",
2759
+ op: "in",
2760
+ value: ["<start_event>", "<middle_event>", "<conversion_event>"],
2761
+ },
2762
+ ],
2763
+ range: { preset: "30d" },
2764
+ sort: [{ field: "label", direction: "asc" }],
2765
+ limit: 3,
2766
+ },
2767
+ },
2768
+ {
2769
+ id: "request_path_funnel",
2770
+ title: "Request path funnel",
2771
+ visualization: "funnel",
2772
+ purpose: "Measure ordered conversion through observed request paths.",
2773
+ usableAsIs: false,
2774
+ placeholders: {
2775
+ paths: ["/<start_path>", "/<middle_path>", "/<conversion_path>"],
2776
+ },
2777
+ adaptWith: [
2778
+ "Use when catalog classification includes a path-based funnel or the source code shows ordered route milestones.",
2779
+ "Replace placeholders with exact observed paths from the selected app.",
2780
+ "Do not include repeated activity routes unless source context says they are milestones.",
2781
+ ],
2782
+ spec: {
2783
+ version: 1,
2784
+ dataset: "events",
2785
+ visualization: "funnel",
2786
+ metric: { name: "request_count" },
2787
+ groupBy: [{ field: "path" }],
2788
+ filters: [
2789
+ {
2790
+ field: "path",
2791
+ op: "in",
2792
+ value: ["/<start_path>", "/<middle_path>", "/<conversion_path>"],
2793
+ },
2794
+ ],
2795
+ range: { preset: "30d" },
2796
+ sort: [{ field: "label", direction: "asc" }],
2797
+ limit: 3,
2798
+ },
2799
+ },
2800
+ {
2801
+ id: "property_breakdown",
2802
+ title: "Property breakdown",
2803
+ visualization: "bar",
2804
+ purpose: "Compare a promoted dimension property observed in events.",
2805
+ usableAsIs: false,
2806
+ placeholders: {
2807
+ property: "<promoted_dimension_property>",
2808
+ },
2809
+ adaptWith: [
2810
+ "Use a property key promoted by capabilities chartSpec.groupBy, such as property:plan.",
2811
+ "Choose stable categorical properties with enough observations to compare.",
2812
+ ],
2813
+ spec: {
2814
+ version: 1,
2815
+ dataset: "events",
2816
+ visualization: "bar",
2817
+ metric: { name: "custom_event_count" },
2818
+ groupBy: [{ field: "property:<promoted_dimension_property>" }],
2819
+ filters: [],
2820
+ range: { preset: "30d" },
2821
+ sort: [{ field: "value", direction: "desc" }],
2822
+ limit: 10,
2823
+ },
2824
+ },
2825
+ {
2826
+ id: "average_metric_property",
2827
+ title: "Average metric property",
2828
+ visualization: "line",
2829
+ purpose: "Track an observed numeric event property over time.",
2830
+ usableAsIs: false,
2831
+ placeholders: {
2832
+ metric: "avg:<promoted_metric_property>",
2833
+ },
2834
+ adaptWith: [
2835
+ "Use a numeric property key promoted by capabilities chartSpec.metrics.",
2836
+ "Prefer averages for scores, durations, quantities, or ordinal values.",
2837
+ ],
2838
+ spec: {
2839
+ version: 1,
2840
+ dataset: "events",
2841
+ visualization: "line",
2842
+ metric: { name: "avg:<promoted_metric_property>" },
2843
+ groupBy: [{ field: "day" }],
2844
+ filters: [],
2845
+ range: { preset: "30d" },
2846
+ sort: [{ field: "label", direction: "asc" }],
2847
+ limit: 30,
2848
+ },
2849
+ },
2850
+ {
2851
+ id: "single_number_total",
2852
+ title: "Single number total",
2853
+ visualization: "number",
2854
+ purpose: "Show one headline count for a selected event or request segment.",
2855
+ usableAsIs: false,
2856
+ placeholders: {
2857
+ eventName: "<event_name>",
2858
+ },
2859
+ adaptWith: [
2860
+ "Use when a dashboard needs a compact KPI card.",
2861
+ "Replace the event filter with an observed conversion or outcome event.",
2862
+ ],
2863
+ spec: {
2864
+ version: 1,
2865
+ dataset: "events",
2866
+ visualization: "number",
2867
+ metric: { name: "custom_event_count" },
2868
+ groupBy: [],
2869
+ filters: [{ field: "event_name", op: "=", value: "<event_name>" }],
2870
+ range: { preset: "30d" },
2871
+ sort: [],
2872
+ limit: 1,
2873
+ },
2874
+ },
2875
+ ];
2876
+ }
2877
+ function runAnalyticsChartsTemplates(command, io) {
2878
+ const templates = analyticsChartTemplates();
2879
+ const payload = {
2880
+ ok: true,
2881
+ command: command.command,
2882
+ templates,
2883
+ next: [
2884
+ "Run tender app analytics capabilities <artifact-id> --include-catalog --range 30d --json.",
2885
+ "Choose a template and replace placeholders with observed events, paths, or promoted property keys.",
2886
+ "Validate with tender app analytics query <artifact-id> --spec chart.json --json.",
2887
+ "Preview saved writes with tender app analytics dashboards ... --dry-run and charts create ... --dry-run.",
2888
+ ],
2889
+ };
2890
+ if (command.json) {
2891
+ io.stdout(JSON.stringify(payload, null, 2));
2892
+ }
2893
+ else {
2894
+ io.stdout([
2895
+ "Analytics chart templates",
2896
+ "",
2897
+ ...templates.map((template) => {
2898
+ const marker = template.usableAsIs ? "usable as-is" : "adapt before use";
2899
+ return `- ${template.id}: ${template.title} (${marker})`;
2900
+ }),
2901
+ "",
2902
+ "Use --json to copy full chart specs.",
2903
+ ].join("\n"));
2904
+ }
2905
+ return 0;
2906
+ }
2602
2907
  function parseAnalyticsChartsCreateArgs(args) {
2603
2908
  if (args.includes("--help") || args.includes("-h")) {
2604
2909
  return { command: "help", topic: "app analytics charts create" };
@@ -2748,6 +3053,18 @@ function parseTenderArgs(args) {
2748
3053
  resourceArgs[3] === "query") {
2749
3054
  return parseAnalyticsExportQueryArgs(resourceArgs.slice(4));
2750
3055
  }
3056
+ if (resourceArgs[1] === "analytics" &&
3057
+ resourceArgs[2] === "charts" &&
3058
+ resourceArgs[3] === "templates") {
3059
+ return parseAnalyticsChartsTemplatesArgs(resourceArgs.slice(4));
3060
+ }
3061
+ if (resourceArgs[1] === "analytics" &&
3062
+ resourceArgs[2] === "charts" &&
3063
+ (resourceArgs[3] === undefined ||
3064
+ resourceArgs[3] === "--help" ||
3065
+ resourceArgs[3] === "-h")) {
3066
+ return { command: "help", topic: "app analytics charts templates" };
3067
+ }
2751
3068
  if (resourceArgs[1] === "analytics" &&
2752
3069
  resourceArgs[2] === "charts" &&
2753
3070
  resourceArgs[3] === "create") {
@@ -5470,6 +5787,9 @@ export async function runTenderCli(args, io = {
5470
5787
  else if (command.topic === "app analytics export query") {
5471
5788
  io.stdout(analyticsExportQueryHelp());
5472
5789
  }
5790
+ else if (command.topic === "app analytics charts templates") {
5791
+ io.stdout(analyticsChartsTemplatesHelp());
5792
+ }
5473
5793
  else if (command.topic === "app analytics charts create") {
5474
5794
  io.stdout(analyticsChartsCreateHelp());
5475
5795
  }
@@ -5568,6 +5888,9 @@ export async function runTenderCli(args, io = {
5568
5888
  if (command.command === "app analytics export query") {
5569
5889
  return await runAnalyticsExportQuery(command, io, runtime);
5570
5890
  }
5891
+ if (command.command === "app analytics charts templates") {
5892
+ return runAnalyticsChartsTemplates(command, io);
5893
+ }
5571
5894
  if (command.command === "app analytics charts create") {
5572
5895
  return await runAnalyticsChartsCreate(command, io, runtime);
5573
5896
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenderprompt/cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tender": "bin/tender.js"