@tenderprompt/cli 0.1.16 → 0.1.17

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 (2) hide show
  1. package/dist/index.js +345 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ const CLI_PACKAGE_NAME = "@tenderprompt/cli";
11
11
  const UNKNOWN_CLI_VERSION = "0.0.0";
12
12
  const RECOMMENDED_TENDER_PROMPT_SKILL = {
13
13
  name: "tender-prompt",
14
- version: "0.1.2",
14
+ version: "0.1.4",
15
15
  source: "git@github.com:tenderprompt/skills.git",
16
16
  path: "skills/tender-prompt/SKILL.md",
17
17
  updateHint: "Refresh the tender-prompt skill from git@github.com:tenderprompt/skills.git when the local skill version is older.",
@@ -82,6 +82,10 @@ Commands:
82
82
  tender auth login --device
83
83
  Authorize this machine with a browser-approved device flow
84
84
  tender auth status Show whether a Tender API token is available
85
+ tender playbooks list List remote Tender Prompt playbook metadata
86
+ tender playbooks get <id> Fetch one remote playbook body on demand
87
+ tender playbooks file <id> --path <path>
88
+ Fetch one playbook reference file on demand
85
89
  tender app list List Tender App records visible to the current token
86
90
  tender app create --name <name>
87
91
  Create a new Tender App in the current account
@@ -134,6 +138,9 @@ Examples:
134
138
  tender capabilities --json
135
139
  tender auth login --device --profile edit-preview --artifact artifact_123 --ttl 7d
136
140
  tender auth status --json
141
+ tender playbooks list --json
142
+ tender playbooks get recharge-bundle-builder --json
143
+ tender playbooks file recharge-bundle-builder --path references/recharge-payloads.md --json
137
144
  tender app list --json
138
145
  tender app create --name "Exit Intent Widget" --json
139
146
  tender app create --name "Exit Intent Widget" --init --dir ./widget --preview --json
@@ -203,6 +210,49 @@ Examples:
203
210
  tender auth status
204
211
  tender auth status --profile publish --json`;
205
212
  }
213
+ function playbooksHelp() {
214
+ return `Usage: tender playbooks <command> [options]
215
+
216
+ Commands:
217
+ tender playbooks list
218
+ List remote playbook metadata only
219
+ tender playbooks get <playbook-id>
220
+ Fetch one playbook body and reference index
221
+ tender playbooks file <playbook-id> --path <path>
222
+ Fetch one playbook reference file
223
+
224
+ Examples:
225
+ tender playbooks list --json
226
+ tender playbooks get recharge-bundle-builder --json
227
+ tender playbooks file recharge-bundle-builder --path references/recharge-payloads.md --json`;
228
+ }
229
+ function playbooksListHelp() {
230
+ return `Usage: tender playbooks list [--base-url <url>] [--token <token>] [--profile <name>] [--json]
231
+
232
+ Lists compact metadata for all remote Tender Prompt playbooks. This command
233
+ does not write playbooks to disk.
234
+
235
+ Examples:
236
+ tender playbooks list --json`;
237
+ }
238
+ function playbooksGetHelp() {
239
+ return `Usage: tender playbooks get <playbook-id> [--base-url <url>] [--token <token>] [--profile <name>] [--json]
240
+
241
+ Fetches one remote playbook body plus a reference-file index. This command
242
+ prints session-only context and does not write to the checkout.
243
+
244
+ Examples:
245
+ tender playbooks get recharge-bundle-builder --json`;
246
+ }
247
+ function playbooksFileHelp() {
248
+ return `Usage: tender playbooks file <playbook-id> --path <references/file.md> [--base-url <url>] [--token <token>] [--profile <name>] [--json]
249
+
250
+ Fetches one playbook reference file. Paths are limited to references/*.md.
251
+ This command does not write playbooks to disk.
252
+
253
+ Examples:
254
+ tender playbooks file recharge-bundle-builder --path references/recharge-payloads.md --json`;
255
+ }
206
256
  function appHelp() {
207
257
  return `Usage: tender app <command> [options]
208
258
 
@@ -916,6 +966,14 @@ function tenderCliCapabilities() {
916
966
  latestRunner: "npm exec --yes @tenderprompt/cli@latest --",
917
967
  },
918
968
  recommendedSkill: RECOMMENDED_TENDER_PROMPT_SKILL,
969
+ playbooks: {
970
+ mode: "remote_progressive_disclosure",
971
+ metadataCommand: "tender playbooks list --json",
972
+ entryCommand: "tender playbooks get <playbook-id> --json",
973
+ fileCommand: "tender playbooks file <playbook-id> --path <path> --json",
974
+ persistence: "session_only",
975
+ writesToCheckout: false,
976
+ },
919
977
  description: "Agent-safe Tender App source, lifecycle, analytics, and local scaffold controls.",
920
978
  discovery: {
921
979
  primary: "npm exec --yes @tenderprompt/cli@latest -- capabilities --json",
@@ -923,6 +981,7 @@ function tenderCliCapabilities() {
923
981
  "tender --version",
924
982
  "tender --help",
925
983
  "tender auth --help",
984
+ "tender playbooks --help",
926
985
  "tender app --help",
927
986
  "tender app agent heartbeat --help",
928
987
  "tender app analytics --help",
@@ -939,6 +998,20 @@ function tenderCliCapabilities() {
939
998
  "tender auth login --device --profile edit-preview --artifact <artifact-id> --ttl 7d --json",
940
999
  ],
941
1000
  },
1001
+ {
1002
+ name: "playbook_discovery",
1003
+ when: "Before planning non-trivial Tender App or widget work.",
1004
+ commands: [
1005
+ "tender playbooks list --json",
1006
+ "tender playbooks get <playbook-id> --json",
1007
+ "tender playbooks file <playbook-id> --path references/<file>.md --json",
1008
+ ],
1009
+ notes: [
1010
+ "List returns metadata only; use it to decide whether a playbook is relevant.",
1011
+ "Fetch full playbooks and reference files only on demand.",
1012
+ "Do not write fetched playbook contents into the checkout or install them as skills.",
1013
+ ],
1014
+ },
942
1015
  {
943
1016
  name: "create_new_app_with_preview",
944
1017
  when: "When the user wants a new Tender App that is immediately visible.",
@@ -1057,6 +1130,24 @@ function tenderCliCapabilities() {
1057
1130
  requiresAuth: false,
1058
1131
  writes: false,
1059
1132
  },
1133
+ {
1134
+ command: "tender playbooks list --json",
1135
+ purpose: "List remote Tender Prompt playbook metadata without fetching full bodies.",
1136
+ requiresAuth: true,
1137
+ writes: false,
1138
+ },
1139
+ {
1140
+ command: "tender playbooks get <playbook-id> --json",
1141
+ purpose: "Fetch one remote playbook body plus its reference index.",
1142
+ requiresAuth: true,
1143
+ writes: false,
1144
+ },
1145
+ {
1146
+ command: "tender playbooks file <playbook-id> --path references/<file>.md --json",
1147
+ purpose: "Fetch one playbook reference file on demand.",
1148
+ requiresAuth: true,
1149
+ writes: false,
1150
+ },
1060
1151
  {
1061
1152
  command: "tender app create --name <name> --init --dir <dir> --preview --json",
1062
1153
  purpose: "Create a new Tender App, initialize API-derived source in a local checkout, push it, and request a preview build.",
@@ -2742,6 +2833,114 @@ function parseAuthStatusArgs(args) {
2742
2833
  json,
2743
2834
  };
2744
2835
  }
2836
+ function parsePlaybooksCommonArgs(args, startIndex) {
2837
+ let baseUrl = null;
2838
+ let flagToken = null;
2839
+ let profileName = null;
2840
+ let json = false;
2841
+ const extras = [];
2842
+ for (let index = startIndex; index < args.length; index += 1) {
2843
+ const arg = args[index];
2844
+ if (arg === "--json") {
2845
+ json = true;
2846
+ continue;
2847
+ }
2848
+ if (arg === "--base-url") {
2849
+ baseUrl = parseBaseUrlFlag(args, index);
2850
+ index += 1;
2851
+ continue;
2852
+ }
2853
+ if (arg === "--token") {
2854
+ flagToken = parseTokenFlag(args, index);
2855
+ index += 1;
2856
+ continue;
2857
+ }
2858
+ if (arg === "--profile") {
2859
+ profileName = parseProfileName(args[index + 1], "--profile");
2860
+ index += 1;
2861
+ continue;
2862
+ }
2863
+ extras.push(arg);
2864
+ }
2865
+ return { baseUrl, flagToken, profileName, json, extras };
2866
+ }
2867
+ function parsePlaybooksListArgs(args) {
2868
+ if (args.includes("--help") || args.includes("-h")) {
2869
+ return { command: "help", topic: "playbooks list" };
2870
+ }
2871
+ const parsed = parsePlaybooksCommonArgs(args, 0);
2872
+ if (parsed.extras.length > 0) {
2873
+ throw new TenderCliUsageError(`Unknown playbooks list option: ${parsed.extras[0]}`);
2874
+ }
2875
+ return {
2876
+ command: "playbooks list",
2877
+ baseUrl: parsed.baseUrl,
2878
+ flagToken: parsed.flagToken,
2879
+ profileName: parsed.profileName,
2880
+ json: parsed.json,
2881
+ };
2882
+ }
2883
+ function parsePlaybooksGetArgs(args) {
2884
+ if (args.includes("--help") || args.includes("-h")) {
2885
+ return { command: "help", topic: "playbooks get" };
2886
+ }
2887
+ const playbookId = args[0];
2888
+ if (!playbookId || playbookId.startsWith("-")) {
2889
+ throw new TenderCliUsageError("playbook id is required. Example: tender playbooks get recharge-bundle-builder --json");
2890
+ }
2891
+ const parsed = parsePlaybooksCommonArgs(args, 1);
2892
+ if (parsed.extras.length > 0) {
2893
+ throw new TenderCliUsageError(`Unknown playbooks get option: ${parsed.extras[0]}`);
2894
+ }
2895
+ return {
2896
+ command: "playbooks get",
2897
+ playbookId,
2898
+ baseUrl: parsed.baseUrl,
2899
+ flagToken: parsed.flagToken,
2900
+ profileName: parsed.profileName,
2901
+ json: parsed.json,
2902
+ };
2903
+ }
2904
+ function parsePlaybooksFileArgs(args) {
2905
+ if (args.includes("--help") || args.includes("-h")) {
2906
+ return { command: "help", topic: "playbooks file" };
2907
+ }
2908
+ const playbookId = args[0];
2909
+ if (!playbookId || playbookId.startsWith("-")) {
2910
+ throw new TenderCliUsageError("playbook id is required. Example: tender playbooks file recharge-bundle-builder --path references/recharge-payloads.md --json");
2911
+ }
2912
+ let filePath = null;
2913
+ const rest = [];
2914
+ for (let index = 1; index < args.length; index += 1) {
2915
+ const arg = args[index];
2916
+ if (arg === "--path") {
2917
+ const value = args[index + 1];
2918
+ if (!value || value.startsWith("-")) {
2919
+ throw new TenderCliUsageError("--path requires a playbook reference path.");
2920
+ }
2921
+ filePath = value;
2922
+ index += 1;
2923
+ continue;
2924
+ }
2925
+ rest.push(arg);
2926
+ }
2927
+ const parsed = parsePlaybooksCommonArgs(rest, 0);
2928
+ if (parsed.extras.length > 0) {
2929
+ throw new TenderCliUsageError(`Unknown playbooks file option: ${parsed.extras[0]}`);
2930
+ }
2931
+ if (!filePath) {
2932
+ throw new TenderCliUsageError("--path is required. Example: tender playbooks file recharge-bundle-builder --path references/recharge-payloads.md --json");
2933
+ }
2934
+ return {
2935
+ command: "playbooks file",
2936
+ playbookId,
2937
+ filePath,
2938
+ baseUrl: parsed.baseUrl,
2939
+ flagToken: parsed.flagToken,
2940
+ profileName: parsed.profileName,
2941
+ json: parsed.json,
2942
+ };
2943
+ }
2745
2944
  function parseGenerateEnvTypesArgs(args) {
2746
2945
  if (args.includes("--help") || args.includes("-h")) {
2747
2946
  return { command: "help", topic: "app generate-env-types" };
@@ -4193,6 +4392,21 @@ function parseTenderArgs(args) {
4193
4392
  }
4194
4393
  throw new TenderCliUsageError(`Unknown auth command: ${args[1]}`);
4195
4394
  }
4395
+ if (args[0] === "playbooks") {
4396
+ if (args[1] === undefined || args[1] === "--help" || args[1] === "-h") {
4397
+ return { command: "help", topic: "playbooks" };
4398
+ }
4399
+ if (args[1] === "list") {
4400
+ return parsePlaybooksListArgs(args.slice(2));
4401
+ }
4402
+ if (args[1] === "get") {
4403
+ return parsePlaybooksGetArgs(args.slice(2));
4404
+ }
4405
+ if (args[1] === "file") {
4406
+ return parsePlaybooksFileArgs(args.slice(2));
4407
+ }
4408
+ throw new TenderCliUsageError(`Unknown playbooks command: ${args[1]}`);
4409
+ }
4196
4410
  if (args[0] === "apps") {
4197
4411
  throw new TenderCliUsageError("Unknown command: apps. Use `tender app ...` for Tender App workflows.");
4198
4412
  }
@@ -4808,6 +5022,40 @@ async function requestDbApi(input) {
4808
5022
  }
4809
5023
  return payload;
4810
5024
  }
5025
+ function encodePathSegments(filePath) {
5026
+ return filePath.split("/").map(encodeURIComponent).join("/");
5027
+ }
5028
+ async function requestPlaybooksApi(input) {
5029
+ const suffix = input.playbooksPath === "list"
5030
+ ? ""
5031
+ : input.playbooksPath === "get"
5032
+ ? `/${encodeURIComponent(input.playbookId)}`
5033
+ : `/${encodeURIComponent(input.playbookId)}/files/${encodePathSegments(input.filePath)}`;
5034
+ const response = await input.fetcher(`${input.baseUrl}/api/v1/playbooks${suffix}`, {
5035
+ method: "GET",
5036
+ headers: {
5037
+ authorization: `Bearer ${input.token}`,
5038
+ accept: "application/json",
5039
+ },
5040
+ });
5041
+ const payload = await parseJsonResponse(response);
5042
+ if (!response.ok || !payload) {
5043
+ const reasonCode = (typeof payload?.reasonCode === "string" && payload.reasonCode) ||
5044
+ `http_${response.status}`;
5045
+ const message = (typeof payload?.message === "string" && payload.message) ||
5046
+ (typeof payload?.reasonCode === "string" && payload.reasonCode) ||
5047
+ `Playbooks request failed with HTTP ${response.status}.`;
5048
+ const authHint = response.status === 401
5049
+ ? "\nAuthenticate first: tender auth login --device --profile edit-preview --save-profile agent --ttl 7d --json"
5050
+ : "";
5051
+ throw new Error([
5052
+ message,
5053
+ `Reason: ${reasonCode}.`,
5054
+ "Example: tender playbooks list --profile agent --json.",
5055
+ ].join("\n") + authHint);
5056
+ }
5057
+ return payload;
5058
+ }
4811
5059
  function dbCorrectedExample(artifactId, dbPath) {
4812
5060
  if (dbPath === "capabilities") {
4813
5061
  return `tender app db capabilities ${artifactId} --json`;
@@ -7389,6 +7637,25 @@ async function resolveAnalyticsCredentials(command, runtime) {
7389
7637
  env: runtime.env ?? process.env,
7390
7638
  });
7391
7639
  }
7640
+ async function resolvePlaybooksCredentials(command, runtime) {
7641
+ try {
7642
+ return await resolveApiCredentials({
7643
+ baseUrl: command.baseUrl,
7644
+ flagToken: command.flagToken,
7645
+ profileName: command.profileName,
7646
+ env: runtime.env ?? process.env,
7647
+ });
7648
+ }
7649
+ catch (error) {
7650
+ if (error instanceof TenderCliUsageError) {
7651
+ throw new TenderCliUsageError([
7652
+ "Playbooks require Tender authentication.",
7653
+ "Run `tender auth login --device --profile edit-preview --save-profile agent --ttl 7d --json`, set TENDER_API_TOKEN, or pass --token <token>.",
7654
+ ].join("\n"));
7655
+ }
7656
+ throw error;
7657
+ }
7658
+ }
7392
7659
  async function resolveDbCredentials(command, runtime) {
7393
7660
  return await resolveApiCredentials({
7394
7661
  baseUrl: command.baseUrl,
@@ -7426,6 +7693,58 @@ function printDbPayload(command, io, payload, fallbackLines) {
7426
7693
  io.stdout(fallbackLines.join("\n"));
7427
7694
  }
7428
7695
  }
7696
+ function printPlaybooksPayload(command, io, payload, fallback) {
7697
+ if (command.json) {
7698
+ io.stdout(JSON.stringify(payload, null, 2));
7699
+ }
7700
+ else {
7701
+ io.stdout(fallback);
7702
+ }
7703
+ }
7704
+ async function runPlaybooksList(command, io, runtime) {
7705
+ const credentials = await resolvePlaybooksCredentials(command, runtime);
7706
+ const payload = await requestPlaybooksApi({
7707
+ playbooksPath: "list",
7708
+ baseUrl: credentials.baseUrl,
7709
+ token: credentials.token,
7710
+ fetcher: runtime.fetcher ?? fetch,
7711
+ });
7712
+ const playbooks = Array.isArray(payload.playbooks) ? payload.playbooks : [];
7713
+ printPlaybooksPayload(command, io, payload, playbooks
7714
+ .map((playbook) => isCliJsonRecord(playbook)
7715
+ ? `${String(playbook.id ?? "")}\t${String(playbook.description ?? "")}`
7716
+ : "")
7717
+ .filter(Boolean)
7718
+ .join("\n") || "No Tender Prompt playbooks found.");
7719
+ return 0;
7720
+ }
7721
+ async function runPlaybooksGet(command, io, runtime) {
7722
+ const credentials = await resolvePlaybooksCredentials(command, runtime);
7723
+ const payload = await requestPlaybooksApi({
7724
+ playbooksPath: "get",
7725
+ playbookId: command.playbookId,
7726
+ baseUrl: credentials.baseUrl,
7727
+ token: credentials.token,
7728
+ fetcher: runtime.fetcher ?? fetch,
7729
+ });
7730
+ printPlaybooksPayload(command, io, payload, typeof payload.body === "string" ? payload.body : JSON.stringify(payload, null, 2));
7731
+ return 0;
7732
+ }
7733
+ async function runPlaybooksFile(command, io, runtime) {
7734
+ const credentials = await resolvePlaybooksCredentials(command, runtime);
7735
+ const payload = await requestPlaybooksApi({
7736
+ playbooksPath: "file",
7737
+ playbookId: command.playbookId,
7738
+ filePath: command.filePath,
7739
+ baseUrl: credentials.baseUrl,
7740
+ token: credentials.token,
7741
+ fetcher: runtime.fetcher ?? fetch,
7742
+ });
7743
+ printPlaybooksPayload(command, io, payload, typeof payload.content === "string"
7744
+ ? payload.content
7745
+ : JSON.stringify(payload, null, 2));
7746
+ return 0;
7747
+ }
7429
7748
  async function runDbCapabilities(command, io, runtime) {
7430
7749
  const credentials = await resolveDbCredentials(command, runtime);
7431
7750
  const payload = await requestDbApi({
@@ -8042,6 +8361,18 @@ export async function runTenderCli(args, io = {
8042
8361
  else if (command.topic === "auth status") {
8043
8362
  io.stdout(authStatusHelp());
8044
8363
  }
8364
+ else if (command.topic === "playbooks") {
8365
+ io.stdout(playbooksHelp());
8366
+ }
8367
+ else if (command.topic === "playbooks list") {
8368
+ io.stdout(playbooksListHelp());
8369
+ }
8370
+ else if (command.topic === "playbooks get") {
8371
+ io.stdout(playbooksGetHelp());
8372
+ }
8373
+ else if (command.topic === "playbooks file") {
8374
+ io.stdout(playbooksFileHelp());
8375
+ }
8045
8376
  else if (command.topic === "auth") {
8046
8377
  io.stdout(authHelp());
8047
8378
  }
@@ -8194,6 +8525,15 @@ export async function runTenderCli(args, io = {
8194
8525
  if (command.command === "auth status") {
8195
8526
  return await runAuthStatus(command, io, runtime);
8196
8527
  }
8528
+ if (command.command === "playbooks list") {
8529
+ return await runPlaybooksList(command, io, runtime);
8530
+ }
8531
+ if (command.command === "playbooks get") {
8532
+ return await runPlaybooksGet(command, io, runtime);
8533
+ }
8534
+ if (command.command === "playbooks file") {
8535
+ return await runPlaybooksFile(command, io, runtime);
8536
+ }
8197
8537
  if (command.command === "app list") {
8198
8538
  return await runArtifactsList(command, io, runtime);
8199
8539
  }
@@ -8318,8 +8658,10 @@ export async function runTenderCli(args, io = {
8318
8658
  }
8319
8659
  catch (error) {
8320
8660
  if (args.includes("--json") &&
8321
- args[0] === "app" &&
8322
- (args[1] === "db" || (args[1] === "agent" && args[2] === "heartbeat"))) {
8661
+ ((args[0] === "app" &&
8662
+ (args[1] === "db" ||
8663
+ (args[1] === "agent" && args[2] === "heartbeat"))) ||
8664
+ args[0] === "playbooks")) {
8323
8665
  io.stdout(JSON.stringify(cliErrorPayload(error, args, command), null, 2));
8324
8666
  return error instanceof TenderCliUsageError ? 2 : 1;
8325
8667
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenderprompt/cli",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tender": "bin/tender.js"