@f-o-h/cli 0.1.34 → 0.1.35

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 +21 -9
  2. package/dist/foh.js +161 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@ AI-operator provisioning CLI for Front Of House.
4
4
 
5
5
  Public mirror: https://github.com/iiko38/front-of-house-cli
6
6
 
7
- Current published baseline: `@f-o-h/cli@0.1.34`
7
+ Current candidate baseline: `@f-o-h/cli@0.1.35`
8
8
 
9
9
  This mirror is a generated release artifact. The private product monorepo is not
10
10
  published here, and no open-source license is granted unless stated separately.
@@ -61,7 +61,7 @@ foh auth login --web --json
61
61
  foh auth login --email "$FOH_EMAIL" --password "$FOH_PASSWORD" --json
62
62
  foh org list --json
63
63
  foh org use --org <org-id> --json
64
- foh setup --org <org-id> --agent-template <template-id> --agent-name "Demo Agent" --json
64
+ FOH_CLI_SPEND_POLICY=no_spend foh setup --org <org-id> --agent-template <template-id> --agent-name "Demo Agent" --phone-mode observe --json
65
65
  foh prove --agent <agent-id> --json --out foh-proof.json
66
66
  foh test run --suite ./suite.yml --agent <agent-id> --json --out foh-test-report.json
67
67
  foh agent replay --file ./transcript-export.json --json
@@ -80,13 +80,25 @@ prints the fallback URL. `auth login --web` starts browser device
80
80
  authorization, opens `/cli-auth`, waits for console approval, and stores the
81
81
  returned short-lived token. Credential auth remains available as fallback.
82
82
 
83
- `foh prove` produces a compact signed proof report across auth, org context,
84
- agent validation, contact phone readiness, voice provider health, widget
85
- channel/embed readiness, widget smoke, and simulation certification. It is
86
- read-only by default; pass `--mutation-mode ensure` or `--repair` only when you
87
- explicitly want proof to ensure missing widget state. Use `--strict` in
88
- automation when holds should fail the command, and `--mission voice` or
89
- `--require-phone` when a voice/contact number is mandatory for the demo.
83
+ `foh prove` produces a compact signed proof report across auth, org context,
84
+ agent validation, contact phone readiness, voice provider health, widget
85
+ channel/embed readiness, widget smoke, and simulation certification. It is
86
+ read-only by default; pass `--mutation-mode ensure` or `--repair` only when you
87
+ explicitly want proof to ensure missing widget state. Use `--strict` in
88
+ automation when holds should fail the command, and `--mission voice` or
89
+ `--require-phone` when a voice/contact number is mandatory for the demo.
90
+
91
+ For mass AI-agent evals and repeated demo rehearsals, keep setup on the free
92
+ scaffold lane:
93
+
94
+ ```bash
95
+ FOH_CLI_SPEND_POLICY=no_spend foh setup --org <org-id> --agent-template <template-id> --agent-name "Demo Agent" --phone-mode observe --json
96
+ ```
97
+
98
+ `--phone-mode observe` checks whether a contact phone already exists without
99
+ buying one. `--phone-mode skip` bypasses the phone step. `--phone-mode purchase`
100
+ is the explicit paid contact path and is fail-closed when
101
+ `FOH_CLI_SPEND_POLICY=no_spend` is set.
90
102
 
91
103
  The CLI defaults to the production API at `https://api.frontofhouse.okii.uk`.
92
104
 
package/dist/foh.js CHANGED
@@ -11055,6 +11055,47 @@ function registerCompliance(program3) {
11055
11055
 
11056
11056
  // src/commands/provision.ts
11057
11057
  var import_crypto = require("crypto");
11058
+
11059
+ // src/lib/spend-policy.ts
11060
+ var FOH_CLI_SPEND_POLICY_ENV = "FOH_CLI_SPEND_POLICY";
11061
+ var NO_SPEND_POLICY = "no_spend";
11062
+ var PAID_RESOURCE_BLOCKED_REASON_CODE = "paid_resource_blocked_by_spend_policy";
11063
+ function resolveCliSpendPolicy(env = process.env) {
11064
+ const value = String(env[FOH_CLI_SPEND_POLICY_ENV] || "").trim().toLowerCase();
11065
+ return value === NO_SPEND_POLICY ? NO_SPEND_POLICY : "default";
11066
+ }
11067
+ function isNoSpendPolicy(env = process.env) {
11068
+ return resolveCliSpendPolicy(env) === NO_SPEND_POLICY;
11069
+ }
11070
+ function buildPaidResourceBlockedEnvelope(input) {
11071
+ return cliEnvelope({
11072
+ status: "blocked",
11073
+ reasonCode: PAID_RESOURCE_BLOCKED_REASON_CODE,
11074
+ summary: "Paid or scarce resource mutation blocked by the active no-spend CLI policy.",
11075
+ ids: { org_id: input.orgId ?? null },
11076
+ checks: [
11077
+ {
11078
+ name: "spend_policy",
11079
+ status: "blocked",
11080
+ reason_code: PAID_RESOURCE_BLOCKED_REASON_CODE,
11081
+ spend_policy: NO_SPEND_POLICY,
11082
+ spend_class: "paid_foh",
11083
+ resource: input.resource
11084
+ }
11085
+ ],
11086
+ nextCommands: input.nextCommands,
11087
+ extra: {
11088
+ spend_policy: NO_SPEND_POLICY,
11089
+ spend_class: "paid_foh",
11090
+ safe_to_retry: false,
11091
+ resource: input.resource,
11092
+ command: input.command,
11093
+ operator_note: "No-spend eval mode allows setup and proof work, but blocks FOH-owned phone-number purchasing. Use an existing/BYON phone path or rerun outside no-spend eval mode with explicit operator approval."
11094
+ }
11095
+ });
11096
+ }
11097
+
11098
+ // src/commands/provision.ts
11058
11099
  function requiredOrg2(opts, step) {
11059
11100
  return resolveRequiredOrgId(opts.org, {
11060
11101
  apiUrl: opts.apiUrl,
@@ -11089,6 +11130,22 @@ function registerProvision(program3) {
11089
11130
  format({ status: "skipped", reason: "already_provisioned", phone_number: onboarding.phone_number }, { json: opts.json ?? false });
11090
11131
  return;
11091
11132
  }
11133
+ if (isNoSpendPolicy()) {
11134
+ format(
11135
+ buildPaidResourceBlockedEnvelope({
11136
+ resource: "foh_twilio_phone_number",
11137
+ command: "foh provision buy",
11138
+ orgId: resolvedOrg,
11139
+ nextCommands: [
11140
+ `foh provision status --org ${resolvedOrg} --json`,
11141
+ `foh prove --mission voice --require-phone --org ${resolvedOrg} --json`
11142
+ ]
11143
+ }),
11144
+ { json: opts.json ?? false }
11145
+ );
11146
+ markCommandFailed(1);
11147
+ return;
11148
+ }
11092
11149
  const data = await apiFetch("/v1/console/org/twilio/provision", {
11093
11150
  method: "POST",
11094
11151
  body: JSON.stringify({ country: opts.country, area_code: opts.areaCode }),
@@ -32698,7 +32755,7 @@ var StdioServerTransport = class {
32698
32755
  };
32699
32756
 
32700
32757
  // src/lib/cli-version.ts
32701
- var CLI_VERSION = "0.1.34";
32758
+ var CLI_VERSION = "0.1.35";
32702
32759
 
32703
32760
  // src/commands/mcp-serve.ts
32704
32761
  var DEFAULT_TIMEOUT_MS = 12e4;
@@ -34331,6 +34388,16 @@ function timedStepResult(result, startedAtIso, startedAtMs) {
34331
34388
  function optionNameToFlag(key) {
34332
34389
  return "--" + key.replace(/([A-Z])/g, "-$1").toLowerCase();
34333
34390
  }
34391
+ function normalizeSetupPhoneMode(raw) {
34392
+ const value = String(raw || "purchase").trim().toLowerCase();
34393
+ if (value === "observe" || value === "skip" || value === "purchase") return value;
34394
+ throw new FohError({
34395
+ step: "setup.phone_mode",
34396
+ error: `Invalid --phone-mode "${String(raw)}"`,
34397
+ remediation: "Use one of: observe, skip, purchase.",
34398
+ reasonCode: "setup_invalid_phone_mode"
34399
+ });
34400
+ }
34334
34401
  function buildMissingOptionsPlan(missing, opts) {
34335
34402
  const missingFlags = missing.map(optionNameToFlag);
34336
34403
  const signInUrl = buildConsoleSignInUrl(resolveConsoleBaseUrl(opts.consoleUrl));
@@ -34381,7 +34448,7 @@ function emitMissingOptionsPlan(missing, opts) {
34381
34448
  }
34382
34449
  }
34383
34450
  function registerSetup(program3) {
34384
- program3.command("setup").description("Fully provision a new agency customer in one command").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--agent-template <id>", "Agent template ID (e.g. viewing-request)").option("--agent-name <name>", "Name for the new agent").option("--phone-country <cc>", "Phone number country code", "GB").option("--phone-area-code <code>", "Phone area code preference").option("--widget-domains <domains>", "Comma-separated widget domain allowlist").option("--voice-provider <p>", "TTS provider: openai, azure, twilio").option("--voice-id <id>", "Voice ID").option("--skip-compliance", "Skip compliance submission and wait").option("--skip-voice", "Skip voice configuration").option("--skip-tests", "Skip smoke tests").option("--cert-mode <m>", "Simulation cert mode: quick, full, stress", "quick").option("--cert-adaptive-runs <n>", "Adaptive run count for certification loop", "30").option("--cert-max-improvement-rounds <n>", "Max instruction improvement rounds in cert loop (0-5)", "1").option("--resume-from <step>", `Resume from a setup step (${SETUP_STEP_ORDER.join(", ")})`).option("--report-out <path>", "Optional path to write signed setup run report JSON").option("--dry-run", "Print all steps that would run without making any API calls").option("--api-url <url>", "API base URL override").option("--console-url <url>", "Console sign-in URL override").option("--json", "Output as JSON").action(async (opts) => {
34451
+ program3.command("setup").description("Fully provision a new agency customer in one command").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--agent-template <id>", "Agent template ID (e.g. viewing-request)").option("--agent-name <name>", "Name for the new agent").option("--phone-country <cc>", "Phone number country code", "GB").option("--phone-area-code <code>", "Phone area code preference").option("--phone-mode <mode>", "Phone path: observe, skip, or purchase", "purchase").option("--widget-domains <domains>", "Comma-separated widget domain allowlist").option("--voice-provider <p>", "TTS provider: openai, azure, twilio").option("--voice-id <id>", "Voice ID").option("--skip-compliance", "Skip compliance submission and wait").option("--skip-voice", "Skip voice configuration").option("--skip-tests", "Skip smoke tests").option("--cert-mode <m>", "Simulation cert mode: quick, full, stress", "quick").option("--cert-adaptive-runs <n>", "Adaptive run count for certification loop", "30").option("--cert-max-improvement-rounds <n>", "Max instruction improvement rounds in cert loop (0-5)", "1").option("--resume-from <step>", `Resume from a setup step (${SETUP_STEP_ORDER.join(", ")})`).option("--report-out <path>", "Optional path to write signed setup run report JSON").option("--dry-run", "Print all steps that would run without making any API calls").option("--api-url <url>", "API base URL override").option("--console-url <url>", "Console sign-in URL override").option("--json", "Output as JSON").action(async (opts) => {
34385
34452
  if (!opts.org) {
34386
34453
  try {
34387
34454
  opts.org = loadCredentials(opts.apiUrl).orgId;
@@ -34425,6 +34492,17 @@ function registerSetup(program3) {
34425
34492
  }
34426
34493
  const certAdaptiveRuns = Math.max(1, Math.min(120, Number(opts.certAdaptiveRuns ?? 30) || 30));
34427
34494
  const certMaxImprovementRounds = Math.max(0, Math.min(5, Number(opts.certMaxImprovementRounds ?? 1) || 1));
34495
+ let phoneMode;
34496
+ try {
34497
+ phoneMode = normalizeSetupPhoneMode(opts.phoneMode);
34498
+ } catch (error2) {
34499
+ if (error2 instanceof FohError) {
34500
+ formatError(error2);
34501
+ markCommandFailed(1);
34502
+ return;
34503
+ }
34504
+ throw error2;
34505
+ }
34428
34506
  const emitSetupReport = (status, failure) => {
34429
34507
  const reportPayload = {
34430
34508
  schema_version: "foh_cli_setup_run.v1",
@@ -34435,6 +34513,8 @@ function registerSetup(program3) {
34435
34513
  completed_at: nowIso(),
34436
34514
  dry_run: Boolean(opts.dryRun),
34437
34515
  resume_from: resumeState.resumeFrom,
34516
+ phone_mode: phoneMode,
34517
+ spend_policy: resolveCliSpendPolicy(),
34438
34518
  org_id: opts.org,
34439
34519
  agent_template: opts.agentTemplate,
34440
34520
  agent_name: opts.agentName,
@@ -34591,6 +34671,20 @@ function registerSetup(program3) {
34591
34671
  return { step: "wait_compliance", status: "done" };
34592
34672
  });
34593
34673
  await step("provision_phone", "Buy a Twilio phone number", async () => {
34674
+ if (phoneMode === "skip") {
34675
+ return {
34676
+ step: "provision_phone",
34677
+ status: "skipped",
34678
+ detail: {
34679
+ reason_code: "phone_mode_skip",
34680
+ phone_mode: phoneMode,
34681
+ spend_policy: resolveCliSpendPolicy(),
34682
+ spend_class: "free",
34683
+ safe_to_retry: true,
34684
+ operator_note: "Phone provisioning was skipped by explicit setup phone mode."
34685
+ }
34686
+ };
34687
+ }
34594
34688
  const onboarding = await apiFetch(`/v1/console/org/${opts.org}/onboarding`, {
34595
34689
  orgId: opts.org,
34596
34690
  apiUrlOverride: opts.apiUrl
@@ -34599,6 +34693,42 @@ function registerSetup(program3) {
34599
34693
  phoneNumber = onboarding.phone_number;
34600
34694
  return { step: "provision_phone", status: "skipped", detail: "already provisioned" };
34601
34695
  }
34696
+ if (phoneMode === "observe") {
34697
+ return {
34698
+ step: "provision_phone",
34699
+ status: "skipped",
34700
+ detail: {
34701
+ reason_code: isNoSpendPolicy() ? "voice_contact_expected_no_spend_hold" : "contact_phone_missing",
34702
+ phone_mode: phoneMode,
34703
+ spend_policy: resolveCliSpendPolicy(),
34704
+ spend_class: "free",
34705
+ safe_to_retry: true,
34706
+ next_commands: [
34707
+ `foh provision status --org ${opts.org} --json`,
34708
+ `foh prove --mission voice --require-phone --org ${opts.org} --json`
34709
+ ],
34710
+ operator_note: "Setup observed phone readiness only; no phone number was purchased."
34711
+ }
34712
+ };
34713
+ }
34714
+ if (isNoSpendPolicy()) {
34715
+ return {
34716
+ step: "provision_phone",
34717
+ status: "skipped",
34718
+ detail: {
34719
+ reason_code: PAID_RESOURCE_BLOCKED_REASON_CODE,
34720
+ spend_policy: NO_SPEND_POLICY,
34721
+ spend_class: "paid_foh",
34722
+ safe_to_retry: false,
34723
+ resource: "foh_twilio_phone_number",
34724
+ next_commands: [
34725
+ `foh provision status --org ${opts.org} --json`,
34726
+ `foh prove --mission voice --require-phone --org ${opts.org} --json`
34727
+ ],
34728
+ operator_note: "Setup continued without buying a FOH-owned phone number because no-spend eval mode is active."
34729
+ }
34730
+ };
34731
+ }
34602
34732
  const result = await apiFetch("/v1/console/org/twilio/provision", {
34603
34733
  method: "POST",
34604
34734
  body: JSON.stringify({ country: opts.phoneCountry ?? "GB", area_code: opts.phoneAreaCode }),
@@ -34861,6 +34991,8 @@ ${serialiseManifest(manifest)}`,
34861
34991
  org_id: opts.org,
34862
34992
  agent_id: agentId ?? null
34863
34993
  },
34994
+ phone_mode: phoneMode,
34995
+ spend_policy: resolveCliSpendPolicy(),
34864
34996
  org_id: opts.org,
34865
34997
  agent_id: agentId,
34866
34998
  phone_number: phoneNumber,
@@ -37164,12 +37296,23 @@ function registerProve(program3) {
37164
37296
  } else if (provisioningStatus === "failed" && (opts.requirePhone || mission === "voice")) {
37165
37297
  checks.push(hold("contact_channel", "contact_phone_provisioning_failed", "Phone/contact provisioning failed for this org.", `foh provision status --org ${ctx.orgId} --json`, {
37166
37298
  provisioning_status: provisioningStatus,
37167
- mission
37299
+ mission,
37300
+ spend_policy: resolveCliSpendPolicy()
37301
+ }));
37302
+ } else if (isNoSpendPolicy() && (opts.requirePhone || mission === "voice")) {
37303
+ checks.push(hold("contact_channel", "voice_contact_expected_no_spend_hold", "No phone/contact number is provisioned; this is expected in no-spend eval mode unless a BYON/customer-approved phone path exists.", `foh provision status --org ${ctx.orgId} --json`, {
37304
+ provisioning_status: provisioningStatus,
37305
+ mission,
37306
+ spend_policy: resolveCliSpendPolicy(),
37307
+ spend_class: "free",
37308
+ safe_to_retry: true,
37309
+ operator_note: "No-spend proof observes contact readiness only. Do not buy a FOH-owned number during mass evals."
37168
37310
  }));
37169
37311
  } else if (opts.requirePhone || mission === "voice") {
37170
37312
  checks.push(hold("contact_channel", "contact_phone_missing", "No phone/contact number is provisioned for this org.", `foh provision buy --org ${ctx.orgId} --json`, {
37171
37313
  provisioning_status: provisioningStatus,
37172
- mission
37314
+ mission,
37315
+ spend_policy: resolveCliSpendPolicy()
37173
37316
  }));
37174
37317
  } else {
37175
37318
  checks.push(skipped("contact_channel", "contact_phone_not_required", "No phone/contact number is provisioned; pass --require-phone to make this a blocker.", `foh provision buy --org ${ctx.orgId} --json`));
@@ -38741,6 +38884,7 @@ function buildCodexExecutorEnv(input) {
38741
38884
  env.npm_config_yes = "true";
38742
38885
  env[EXTERNAL_AGENT_RUN_DIR_ENV] = input.runDir;
38743
38886
  env[EXTERNAL_AGENT_PROMPT_VERSION_ENV] = input.promptVersion;
38887
+ env[FOH_CLI_SPEND_POLICY_ENV] = NO_SPEND_POLICY;
38744
38888
  env.FOH_CLI_SUPPRESS_BANNER = "1";
38745
38889
  return env;
38746
38890
  }
@@ -39049,9 +39193,15 @@ function classifyRun(input) {
39049
39193
  ...Array.isArray(record2.check_reason_codes) ? record2.check_reason_codes.map((code) => String(code || "")) : []
39050
39194
  ]).filter(Boolean);
39051
39195
  const hasCommandReason = (pattern) => commandReasonCodes.some((reason) => pattern.test(reason));
39196
+ if (hasCommandReason(new RegExp(PAID_RESOURCE_BLOCKED_REASON_CODE, "i"))) {
39197
+ return { status: "hold", reasonCode: PAID_RESOURCE_BLOCKED_REASON_CODE };
39198
+ }
39052
39199
  if (hasCommandReason(/contact_phone_provisioning_failed/i)) {
39053
39200
  return { status: "hold", reasonCode: "voice_contact_phone_provisioning_failed" };
39054
39201
  }
39202
+ if (hasCommandReason(/voice_contact_expected_no_spend_hold/i)) {
39203
+ return { status: "hold", reasonCode: "voice_contact_expected_no_spend_hold" };
39204
+ }
39055
39205
  if (hasCommandReason(/contact_phone_missing/i)) {
39056
39206
  return { status: "hold", reasonCode: "voice_contact_phone_missing" };
39057
39207
  }
@@ -39083,6 +39233,12 @@ ${stderr}`;
39083
39233
  if (/contact_phone_provisioning_failed/i.test(combined)) {
39084
39234
  return { status: "hold", reasonCode: "voice_contact_phone_provisioning_failed" };
39085
39235
  }
39236
+ if (new RegExp(PAID_RESOURCE_BLOCKED_REASON_CODE, "i").test(combined)) {
39237
+ return { status: "hold", reasonCode: PAID_RESOURCE_BLOCKED_REASON_CODE };
39238
+ }
39239
+ if (/voice_contact_expected_no_spend_hold/i.test(combined)) {
39240
+ return { status: "hold", reasonCode: "voice_contact_expected_no_spend_hold" };
39241
+ }
39086
39242
  if (/contact_phone_missing/i.test(combined)) {
39087
39243
  return { status: "hold", reasonCode: "voice_contact_phone_missing" };
39088
39244
  }
@@ -39293,7 +39449,7 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
39293
39449
  var DEFAULT_PROMPT_VERSION = "blank-setup.v1";
39294
39450
  var DEFAULT_BATCH_MODELS = "openai/codex,anthropic/claude,cursor/agent";
39295
39451
  var PROMPTS = {
39296
- "blank-setup.v1": "Go to https://frontofhouse.okii.uk. Use only public docs, public API docs, and the public npm CLI package. Always invoke the CLI with `npx --yes @f-o-h/cli@latest ...`; do not use unpinned `npx @f-o-h/cli ...`, because cached older packages can produce invalid evidence. Install or verify the FOH CLI, authenticate or reach a deterministic auth blocker, then create or configure a Front Of House voice agent and website widget. Prefer the certification-oriented buyer templates: run `npx --yes @f-o-h/cli@latest templates list --category buyer --json` and use `UK Buyer Qualification` or `Viewing Booking` when available; do not use a greeting-only template for proof/certification. Prefer `npx --yes @f-o-h/cli@latest setup` for end-to-end provisioning because it can provision the contact phone path, widget, voice config, smoke test, certification, and publish readiness together. Run proof/smoke/certification where available, including widget proof and voice proof. If voice proof returns `contact_phone_missing`, run the printed provision command or report that exact reason code. Produce a final evidence summary with commands run, docs used, artifacts created, and any blocker reason codes. Do not assume access to the private source repository.",
39452
+ "blank-setup.v1": "Go to https://frontofhouse.okii.uk. Use only public docs, public API docs, and the public npm CLI package. Always invoke the CLI with `npx --yes @f-o-h/cli@latest ...`; do not use unpinned `npx @f-o-h/cli ...`, because cached older packages can produce invalid evidence. Install or verify the FOH CLI, authenticate or reach a deterministic auth blocker, then create or configure a Front Of House voice agent and website widget. Prefer the certification-oriented buyer templates: run `npx --yes @f-o-h/cli@latest templates list --category buyer --json` and use `UK Buyer Qualification` or `Viewing Booking` when available; do not use a greeting-only template for proof/certification. Prefer `npx --yes @f-o-h/cli@latest setup --phone-mode observe` for the free scaffold path: agent, widget, voice config, smoke test, certification, and publish readiness together. Treat phone-number purchasing as an explicit paid/scarce contact-path step, not part of high-volume eval setup. If `FOH_CLI_SPEND_POLICY=no_spend` is active and a command returns `paid_resource_blocked_by_spend_policy`, do not try to bypass it; continue widget/setup proof and report that exact reason code for the phone path. Run proof/smoke/certification where available, including widget proof and voice proof. If voice proof returns `contact_phone_missing` or `voice_contact_expected_no_spend_hold`, report that exact reason code unless a BYON/customer-approved phone path already exists. Produce a final evidence summary with commands run, docs used, artifacts created, and any blocker reason codes. Do not assume access to the private source repository.",
39297
39453
  "debug-proof-failure.v1": "You are given a FOH proof or debug artifact. Use public docs and FOH CLI/API behavior to classify whether the blocker is docs, auth, org setup, agent config, widget, channel, runtime, or product bug. Produce a redacted improvement packet or the exact command needed to produce one. Do not ask the human to interpret logs manually unless no machine-readable artifact exists.",
39298
39454
  "knowledge-miss.v1": "A FOH agent failed to answer a business question. Use CLI/API/docs to determine whether this is a knowledge-ingestion issue, retrieval issue, config issue, prompt/behavior issue, or runtime issue. Prefer foh knowledge query, transcript export, replay, and foh bug improve artifacts over screenshots.",
39299
39455
  "replay-failure.v1": "You are given a FOH transcript or replay artifact. Use CLI/API/docs to replay or inspect the failed interaction, identify expected vs actual behavior, and produce a scenario-test or improvement-packet candidate."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@f-o-h/cli",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "FOH CLI - AI-operator provisioning tool for Front Of House",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {