@skillrecordings/cli 0.16.0 → 0.17.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/dist/index.js CHANGED
@@ -20,8 +20,9 @@ import {
20
20
  runPipeline,
21
21
  validate,
22
22
  validateSync
23
- } from "./chunk-A5RBWKVF.js";
23
+ } from "./chunk-J4IC3GC3.js";
24
24
  import "./chunk-HK3PEWFD.js";
25
+ import "./chunk-WYKL32C3.js";
25
26
  import {
26
27
  createFilterStats,
27
28
  formatFilterStats,
@@ -39,20 +40,26 @@ import {
39
40
  getApp,
40
41
  getOutcomeHistory,
41
42
  getRedis
42
- } from "./chunk-L6YTBYNV.js";
43
+ } from "./chunk-IR6KG25Y.js";
43
44
  import "./chunk-KEV3QKXP.js";
44
- import "./chunk-ZNF7XD2S.js";
45
- import {
46
- upsertVector
47
- } from "./chunk-H3D6VCME.js";
48
- import "./chunk-F4EM72IH.js";
49
- import "./chunk-WYKL32C3.js";
50
45
  import {
51
46
  MemoryService,
52
47
  VotingService,
53
48
  calculateConfidence
54
49
  } from "./chunk-MLNDSBZ4.js";
50
+ import "./chunk-ZNF7XD2S.js";
51
+ import {
52
+ upsertVector
53
+ } from "./chunk-H3D6VCME.js";
55
54
  import "./chunk-MG37YDAK.js";
55
+ import "./chunk-F4EM72IH.js";
56
+ import {
57
+ addShellIntegration,
58
+ autoBootstrapKeychain,
59
+ getKeychainStatus,
60
+ isKeychainSupported,
61
+ storeInKeychain
62
+ } from "./chunk-7SQU7KCI.js";
56
63
  import {
57
64
  OnePasswordProvider,
58
65
  SECRET_REFS,
@@ -74630,19 +74637,43 @@ async function decryptEnvFile(encryptedPath) {
74630
74637
  }
74631
74638
  }
74632
74639
  async function getAgeKeyFrom1Password() {
74633
- if (!process.env.OP_SERVICE_ACCOUNT_TOKEN) {
74634
- return null;
74640
+ if (process.env.SKILL_AGE_KEY) {
74641
+ return process.env.SKILL_AGE_KEY;
74635
74642
  }
74636
74643
  try {
74637
- const { OnePasswordProvider: OnePasswordProvider2 } = await import("./secrets-MGVPGMFJ.js");
74638
- const op = new OnePasswordProvider2();
74639
- if (!await op.isAvailable()) {
74640
- return null;
74644
+ const { getFromKeychain, storeInKeychain: storeInKeychain2, autoBootstrapKeychain: autoBootstrapKeychain2 } = await import("./keychain-IEZHT5WN.js");
74645
+ const fromKeychain = getFromKeychain("age-private-key");
74646
+ if (fromKeychain) return fromKeychain;
74647
+ let opToken = process.env.OP_SERVICE_ACCOUNT_TOKEN;
74648
+ if (!opToken) {
74649
+ opToken = autoBootstrapKeychain2() ?? void 0;
74650
+ }
74651
+ if (opToken) {
74652
+ const originalEnv = process.env.OP_SERVICE_ACCOUNT_TOKEN;
74653
+ process.env.OP_SERVICE_ACCOUNT_TOKEN = opToken;
74654
+ try {
74655
+ const { OnePasswordProvider: OnePasswordProvider2 } = await import("./secrets-MGVPGMFJ.js");
74656
+ const op = new OnePasswordProvider2();
74657
+ if (await op.isAvailable()) {
74658
+ const key = await op.resolve(
74659
+ "op://Support/skill-cli-age-key/private_key"
74660
+ );
74661
+ if (key) {
74662
+ storeInKeychain2("age-private-key", key);
74663
+ return key;
74664
+ }
74665
+ }
74666
+ } finally {
74667
+ if (originalEnv) {
74668
+ process.env.OP_SERVICE_ACCOUNT_TOKEN = originalEnv;
74669
+ } else {
74670
+ delete process.env.OP_SERVICE_ACCOUNT_TOKEN;
74671
+ }
74672
+ }
74641
74673
  }
74642
- return await op.resolve("op://Support/skill-cli-age-key/private_key");
74643
74674
  } catch {
74644
- return null;
74645
74675
  }
74676
+ return null;
74646
74677
  }
74647
74678
  async function loadShippedDefaults(cliRoot2) {
74648
74679
  const encryptedPath = resolve(cliRoot2, ".env.encrypted");
@@ -74719,19 +74750,19 @@ var ROOT_DESCRIPTIONS = {
74719
74750
  minimal: "Skill Recordings support agent CLI. Try: skill wizard, skill keys, skill front inbox, skill front triage. Use --help for details."
74720
74751
  };
74721
74752
  var FRONT_DESCRIPTIONS = {
74722
- full: "Front conversations, inboxes, tags, archival, and reporting.\n\n Prerequisites:\n FRONT_API_TOKEN must be set. Run: skill auth setup\n\n Start here:\n skill front inbox See unassigned conversations\n skill front inbox support List conversations in a specific inbox\n skill front triage AI-powered categorization of inbox items\n\n Investigate a conversation:\n skill front conversation <id> -m Full conversation with messages\n skill front message <id> Single message details + body\n\n Take action:\n skill front assign <id> Assign to a teammate\n skill front reply <id> Draft a reply (HITL, never auto-sends)\n skill front tag <id> Add a tag\n skill front archive <id> Archive a resolved conversation\n\n Bulk operations:\n skill front bulk-archive Archive old/spam conversations\n skill front report Volume + tag + sender forensics\n\n All commands accept --json for HATEOAS-enriched output with _links and _actions.",
74723
- abbreviated: "Front API workflows for inbox triage and conversation actions.\n\n Common:\n skill front inbox\n skill front triage\n skill front conversation <id> -m\n skill front reply <id>\n skill front archive <id>\n\n Requires FRONT_API_TOKEN (skill auth setup).",
74724
- minimal: "Front API commands (inbox, triage, assign, reply, archive). Requires FRONT_API_TOKEN."
74753
+ full: "Front conversations, inboxes, tags, archival, and reporting.\n\n Start here:\n skill front inbox See unassigned conversations\n skill front inbox support List conversations in a specific inbox\n skill front triage AI-powered categorization of inbox items\n\n Investigate a conversation:\n skill front conversation <id> -m Full conversation with messages\n skill front message <id> Single message details + body\n\n Take action:\n skill front assign <id> Assign to a teammate\n skill front reply <id> Draft a reply (HITL, never auto-sends)\n skill front tag <id> Add a tag\n skill front archive <id> Archive a resolved conversation\n\n Bulk operations:\n skill front bulk-archive Archive old/spam conversations\n skill front report Volume + tag + sender forensics\n\n All commands accept --json for HATEOAS-enriched output with _links and _actions.",
74754
+ abbreviated: "Front API workflows for inbox triage and conversation actions.\n\n Common:\n skill front inbox\n skill front triage\n skill front conversation <id> -m\n skill front reply <id>\n skill front archive <id>",
74755
+ minimal: "Front API commands (inbox, triage, assign, reply, archive)."
74725
74756
  };
74726
74757
  var AUTH_DESCRIPTIONS = {
74727
- full: "Manage CLI authentication and secrets.\n\n First time? Start here:\n skill auth setup Interactive wizard \u2014 connects to 1Password vault,\n configures FRONT_API_TOKEN, DATABASE_URL, and more.\n Requires: `op` CLI installed (brew install 1password-cli)\n\n Check your setup:\n skill auth status Shows which secrets are configured and which are missing\n skill auth whoami Verify your 1Password service account identity\n\n All Skill Recordings employees have access to the Support vault in 1Password.\n The setup wizard handles everything \u2014 no manual token pasting needed.",
74728
- abbreviated: "Manage CLI authentication and 1Password secrets.\n\n Start:\n skill auth setup\n skill auth status\n skill auth whoami\n\n Requires the 1Password CLI (`op`).",
74729
- minimal: "Auth and 1Password setup commands."
74758
+ full: "View CLI authentication status.\n\n Check your setup:\n skill auth status Shows which secrets are loaded\n skill auth whoami Verify your 1Password service account identity\n\n API keys ship with the CLI (encrypted). To override with personal keys:\n skill keys Manage personal API key overrides",
74759
+ abbreviated: "View CLI authentication status.\n\n Commands:\n skill auth status\n skill auth whoami",
74760
+ minimal: "Auth status commands (auth status, auth whoami)."
74730
74761
  };
74731
74762
  var INNGEST_DESCRIPTIONS = {
74732
- full: "Inngest event and workflow commands.\n\n Debug pipeline runs:\n skill inngest runs --status failed --after 1h Recent failures\n skill inngest events --after 12h Recent events\n skill inngest investigate <run-id> Deep-dive a specific run\n\n Requires: INNGEST_SIGNING_KEY, INNGEST_EVENT_KEY in env",
74733
- abbreviated: "Inngest events and workflow runs.\n\n Common:\n skill inngest runs --status failed --after 1h\n skill inngest events --after 12h\n skill inngest investigate <run-id>\n\n Requires INNGEST_SIGNING_KEY, INNGEST_EVENT_KEY.",
74734
- minimal: "Inngest events and runs debugging. Requires INNGEST_SIGNING_KEY and INNGEST_EVENT_KEY."
74763
+ full: "Inngest event and workflow commands.\n\n Debug pipeline runs:\n skill inngest runs --status failed --after 1h Recent failures\n skill inngest events --after 12h Recent events\n skill inngest investigate <run-id> Deep-dive a specific run",
74764
+ abbreviated: "Inngest events and workflow runs.\n\n Common:\n skill inngest runs --status failed --after 1h\n skill inngest events --after 12h\n skill inngest investigate <run-id>",
74765
+ minimal: "Inngest events and runs debugging."
74735
74766
  };
74736
74767
  var GROUP_DESCRIPTIONS = {
74737
74768
  root: ROOT_DESCRIPTIONS,
@@ -75667,6 +75698,74 @@ function registerAuthCommands(program3, usageState2) {
75667
75698
  // src/commands/axiom/index.ts
75668
75699
  init_esm_shims();
75669
75700
 
75701
+ // src/core/spinner.ts
75702
+ init_esm_shims();
75703
+ var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
75704
+ var INTERVAL = 80;
75705
+ function createSpinner() {
75706
+ const isTTY = process.stderr.isTTY && !process.env.CI;
75707
+ let interval = null;
75708
+ let frameIndex = 0;
75709
+ let currentMessage = "";
75710
+ const clear = () => {
75711
+ if (!isTTY) return;
75712
+ process.stderr.write("\r\x1B[K");
75713
+ };
75714
+ const render3 = () => {
75715
+ if (!isTTY) return;
75716
+ const frame = FRAMES[frameIndex];
75717
+ process.stderr.write(`\r${frame} ${currentMessage}`);
75718
+ frameIndex = (frameIndex + 1) % FRAMES.length;
75719
+ };
75720
+ const start = (message = "Loading...") => {
75721
+ if (!isTTY) return;
75722
+ currentMessage = message;
75723
+ frameIndex = 0;
75724
+ render3();
75725
+ interval = setInterval(render3, INTERVAL);
75726
+ };
75727
+ const update2 = (message) => {
75728
+ currentMessage = message;
75729
+ if (isTTY && !interval) {
75730
+ render3();
75731
+ }
75732
+ };
75733
+ const stop = () => {
75734
+ if (interval) {
75735
+ clearInterval(interval);
75736
+ interval = null;
75737
+ }
75738
+ clear();
75739
+ };
75740
+ const succeed = (message) => {
75741
+ stop();
75742
+ if (isTTY && message) {
75743
+ process.stderr.write(`\u2713 ${message}
75744
+ `);
75745
+ }
75746
+ };
75747
+ const fail = (message) => {
75748
+ stop();
75749
+ if (isTTY && message) {
75750
+ process.stderr.write(`\u2717 ${message}
75751
+ `);
75752
+ }
75753
+ };
75754
+ return { start, update: update2, stop, succeed, fail };
75755
+ }
75756
+ async function withSpinner(message, fn) {
75757
+ const spinner = createSpinner();
75758
+ spinner.start(message);
75759
+ try {
75760
+ const result = await fn();
75761
+ spinner.stop();
75762
+ return result;
75763
+ } catch (error) {
75764
+ spinner.stop();
75765
+ throw error;
75766
+ }
75767
+ }
75768
+
75670
75769
  // src/lib/axiom-client.ts
75671
75770
  init_esm_shims();
75672
75771
  import { Axiom } from "@axiomhq/js";
@@ -76365,10 +76464,13 @@ async function runQuery(ctx, apl, options) {
76365
76464
  const { startTime, endTime } = parseTimeRange(options.since ?? "24h");
76366
76465
  const outputJson = options.json === true || ctx.format === "json";
76367
76466
  try {
76368
- const result = await client.query(apl, {
76369
- startTime: startTime.toISOString(),
76370
- endTime: endTime.toISOString()
76371
- });
76467
+ const result = await withSpinner(
76468
+ "Running query...",
76469
+ () => client.query(apl, {
76470
+ startTime: startTime.toISOString(),
76471
+ endTime: endTime.toISOString()
76472
+ })
76473
+ );
76372
76474
  if (outputJson) {
76373
76475
  ctx.output.data(result);
76374
76476
  return;
@@ -76412,10 +76514,13 @@ async function listAgentRuns(ctx, options) {
76412
76514
  | sort by _time desc
76413
76515
  | limit ${limit2}`;
76414
76516
  try {
76415
- const result = await client.query(apl, {
76416
- startTime: startTime.toISOString(),
76417
- endTime: endTime.toISOString()
76418
- });
76517
+ const result = await withSpinner(
76518
+ "Loading agent runs...",
76519
+ () => client.query(apl, {
76520
+ startTime: startTime.toISOString(),
76521
+ endTime: endTime.toISOString()
76522
+ })
76523
+ );
76419
76524
  const matches = result.matches ?? [];
76420
76525
  if (outputJson) {
76421
76526
  ctx.output.data(matches.map((m) => m.data));
@@ -76469,10 +76574,13 @@ async function listErrors(ctx, options) {
76469
76574
  | sort by _time desc
76470
76575
  | limit ${limit2}`;
76471
76576
  try {
76472
- const result = await client.query(apl, {
76473
- startTime: startTime.toISOString(),
76474
- endTime: endTime.toISOString()
76475
- });
76577
+ const result = await withSpinner(
76578
+ "Loading errors...",
76579
+ () => client.query(apl, {
76580
+ startTime: startTime.toISOString(),
76581
+ endTime: endTime.toISOString()
76582
+ })
76583
+ );
76476
76584
  const matches = result.matches ?? [];
76477
76585
  if (outputJson) {
76478
76586
  ctx.output.data(matches.map((m) => m.data));
@@ -76509,10 +76617,13 @@ async function getConversation(ctx, conversationId, options) {
76509
76617
  | where conversationId == '${conversationId}'
76510
76618
  | sort by _time asc`;
76511
76619
  try {
76512
- const result = await client.query(apl, {
76513
- startTime: startTime.toISOString(),
76514
- endTime: endTime.toISOString()
76515
- });
76620
+ const result = await withSpinner(
76621
+ "Loading conversation...",
76622
+ () => client.query(apl, {
76623
+ startTime: startTime.toISOString(),
76624
+ endTime: endTime.toISOString()
76625
+ })
76626
+ );
76516
76627
  const matches = result.matches ?? [];
76517
76628
  if (outputJson) {
76518
76629
  ctx.output.data(
@@ -76568,10 +76679,13 @@ async function getClassificationStats(ctx, options) {
76568
76679
  apl += `
76569
76680
  | summarize count = count() by category, complexity`;
76570
76681
  try {
76571
- const result = await client.query(apl, {
76572
- startTime: startTime.toISOString(),
76573
- endTime: endTime.toISOString()
76574
- });
76682
+ const result = await withSpinner(
76683
+ "Loading classifications...",
76684
+ () => client.query(apl, {
76685
+ startTime: startTime.toISOString(),
76686
+ endTime: endTime.toISOString()
76687
+ })
76688
+ );
76575
76689
  const buckets = result.buckets?.totals ?? [];
76576
76690
  if (outputJson) {
76577
76691
  ctx.output.data(buckets);
@@ -76631,10 +76745,13 @@ async function listWorkflowSteps(ctx, options) {
76631
76745
  | sort by _time desc
76632
76746
  | limit ${limit2}`;
76633
76747
  try {
76634
- const result = await client.query(apl, {
76635
- startTime: startTime.toISOString(),
76636
- endTime: endTime.toISOString()
76637
- });
76748
+ const result = await withSpinner(
76749
+ "Loading workflow steps...",
76750
+ () => client.query(apl, {
76751
+ startTime: startTime.toISOString(),
76752
+ endTime: endTime.toISOString()
76753
+ })
76754
+ );
76638
76755
  const matches = result.matches ?? [];
76639
76756
  if (outputJson) {
76640
76757
  ctx.output.data(matches.map((m) => m.data));
@@ -76677,10 +76794,13 @@ async function listApprovals(ctx, options) {
76677
76794
  | sort by _time desc
76678
76795
  | limit ${limit2}`;
76679
76796
  try {
76680
- const result = await client.query(apl, {
76681
- startTime: startTime.toISOString(),
76682
- endTime: endTime.toISOString()
76683
- });
76797
+ const result = await withSpinner(
76798
+ "Loading approvals...",
76799
+ () => client.query(apl, {
76800
+ startTime: startTime.toISOString(),
76801
+ endTime: endTime.toISOString()
76802
+ })
76803
+ );
76684
76804
  const matches = result.matches ?? [];
76685
76805
  if (outputJson) {
76686
76806
  ctx.output.data(matches.map((m) => m.data));
@@ -81226,7 +81346,7 @@ async function runValidateEval(ctx, scenarios, options) {
81226
81346
  return results;
81227
81347
  }
81228
81348
  async function runE2EEval(ctx, scenarios, options) {
81229
- const { runPipeline: runPipeline2 } = await import("./pipeline-FGI6ICWM.js");
81349
+ const { runPipeline: runPipeline2 } = await import("./pipeline-TMFQSA7X.js");
81230
81350
  const concurrency = options.parallel || 1;
81231
81351
  let completed = 0;
81232
81352
  const outputJson = options.outputJson ?? false;
@@ -94707,7 +94827,10 @@ async function listInboxes(ctx, options) {
94707
94827
  const idsOnly = options.idsOnly === true;
94708
94828
  try {
94709
94829
  const front = getFrontClient(ctx);
94710
- const inboxList = await front.inboxes.list();
94830
+ const inboxList = await withSpinner(
94831
+ "Loading inboxes...",
94832
+ () => front.inboxes.list()
94833
+ );
94711
94834
  const inboxes = inboxList._results ?? [];
94712
94835
  if (idsOnly) {
94713
94836
  for (const inbox of inboxes) {
@@ -94771,7 +94894,10 @@ async function listConversations(ctx, inboxNameOrId, options) {
94771
94894
  const idsOnly = options.idsOnly === true;
94772
94895
  try {
94773
94896
  const front = getFrontClient(ctx);
94774
- const inbox = await findInbox(ctx, inboxNameOrId);
94897
+ const inbox = await withSpinner(
94898
+ "Finding inbox...",
94899
+ () => findInbox(ctx, inboxNameOrId)
94900
+ );
94775
94901
  if (!inbox) {
94776
94902
  throw new CLIError({
94777
94903
  userMessage: `Inbox not found: ${inboxNameOrId}`,
@@ -97235,7 +97361,10 @@ async function listEvents(ctx, options) {
97235
97361
  });
97236
97362
  }
97237
97363
  params.limit = limit2;
97238
- const response = await client.listEvents(params);
97364
+ const response = await withSpinner(
97365
+ "Loading events...",
97366
+ () => client.listEvents(params)
97367
+ );
97239
97368
  if (outputJson) {
97240
97369
  ctx.output.data(response.data);
97241
97370
  } else {
@@ -97258,10 +97387,10 @@ async function getEvent(ctx, id, options) {
97258
97387
  const outputJson = options.json === true || ctx.format === "json";
97259
97388
  try {
97260
97389
  const client = new InngestClient({ dev: options.dev });
97261
- const [event, runs] = await Promise.all([
97262
- client.getEvent(id),
97263
- client.getEventRuns(id)
97264
- ]);
97390
+ const [event, runs] = await withSpinner(
97391
+ "Loading event...",
97392
+ () => Promise.all([client.getEvent(id), client.getEventRuns(id)])
97393
+ );
97265
97394
  if (outputJson) {
97266
97395
  ctx.output.data({ event, runs });
97267
97396
  } else {
@@ -97691,7 +97820,7 @@ async function runCommand(ctx, id, options) {
97691
97820
  try {
97692
97821
  const isDev = options.dev ?? await detectDevServer();
97693
97822
  const client = new InngestClient({ dev: isDev });
97694
- const run3 = await client.getRun(id);
97823
+ const run3 = await withSpinner("Loading run...", () => client.getRun(id));
97695
97824
  if (outputJson) {
97696
97825
  ctx.output.data(run3);
97697
97826
  } else {
@@ -114094,14 +114223,138 @@ function registerKeysCommands(program3) {
114094
114223
  }
114095
114224
  }
114096
114225
  });
114226
+ keys.command("setup").description("Set up keychain + shell integration (tries everything)").option("--json", "Output as JSON").action(async (options, command) => {
114227
+ const ctx = await buildContext4(command, options.json);
114228
+ const outputJson = options.json || ctx.format === "json";
114229
+ const OP_VAULT_LINK = "https://start.1password.com/open/i?a=GCTJE4MRGFHKRAYXCEXKZKCEFU&v=u3ujzar6l3nahlahsuzfvg7vcq&i=3e4ip354ps3mhq2wwt6vmtm2zu&h=egghead.1password.com";
114230
+ const status = getKeychainStatus();
114231
+ const steps = [];
114232
+ const errors2 = [];
114233
+ if (status.opTokenInKeychain && status.ageKeyInKeychain && status.shellIntegration) {
114234
+ if (outputJson) {
114235
+ ctx.output.data({ success: true, status: "configured" });
114236
+ } else {
114237
+ ctx.output.data("\u2713 Already configured");
114238
+ }
114239
+ return;
114240
+ }
114241
+ let opToken = null;
114242
+ if (process.env.OP_SERVICE_ACCOUNT_TOKEN) {
114243
+ opToken = process.env.OP_SERVICE_ACCOUNT_TOKEN;
114244
+ steps.push("\u2713 OP token from env");
114245
+ }
114246
+ if (!opToken) {
114247
+ const { getFromKeychain } = await import("./keychain-IEZHT5WN.js");
114248
+ opToken = getFromKeychain("op-service-account-token");
114249
+ if (opToken) steps.push("\u2713 OP token from keychain");
114250
+ }
114251
+ if (!opToken && status.opCliAvailable) {
114252
+ opToken = autoBootstrapKeychain();
114253
+ if (opToken) steps.push("\u2713 OP token from op CLI");
114254
+ }
114255
+ if (opToken && !status.opTokenInKeychain && isKeychainSupported()) {
114256
+ if (storeInKeychain("op-service-account-token", opToken)) {
114257
+ steps.push("\u2713 OP token \u2192 keychain");
114258
+ } else {
114259
+ errors2.push("Could not store OP token in keychain");
114260
+ }
114261
+ }
114262
+ let ageKey = null;
114263
+ if (process.env.SKILL_AGE_KEY) {
114264
+ ageKey = process.env.SKILL_AGE_KEY;
114265
+ steps.push("\u2713 Age key from env");
114266
+ }
114267
+ if (!ageKey) {
114268
+ const { getFromKeychain } = await import("./keychain-IEZHT5WN.js");
114269
+ ageKey = getFromKeychain("age-private-key");
114270
+ if (ageKey) steps.push("\u2713 Age key from keychain");
114271
+ }
114272
+ if (!ageKey && opToken) {
114273
+ const originalEnv = process.env.OP_SERVICE_ACCOUNT_TOKEN;
114274
+ process.env.OP_SERVICE_ACCOUNT_TOKEN = opToken;
114275
+ try {
114276
+ const { OnePasswordProvider: OnePasswordProvider2 } = await import("./secrets-MGVPGMFJ.js");
114277
+ const op = new OnePasswordProvider2();
114278
+ if (await op.isAvailable()) {
114279
+ ageKey = await op.resolve(
114280
+ "op://Support/skill-cli-age-key/private_key"
114281
+ );
114282
+ if (ageKey) steps.push("\u2713 Age key from 1Password");
114283
+ }
114284
+ } catch {
114285
+ errors2.push("1Password SDK failed");
114286
+ } finally {
114287
+ if (originalEnv) {
114288
+ process.env.OP_SERVICE_ACCOUNT_TOKEN = originalEnv;
114289
+ } else {
114290
+ delete process.env.OP_SERVICE_ACCOUNT_TOKEN;
114291
+ }
114292
+ }
114293
+ }
114294
+ if (ageKey && !status.ageKeyInKeychain && isKeychainSupported()) {
114295
+ if (storeInKeychain("age-private-key", ageKey)) {
114296
+ steps.push("\u2713 Age key \u2192 keychain");
114297
+ } else {
114298
+ errors2.push("Could not store age key in keychain");
114299
+ }
114300
+ }
114301
+ if (!status.shellIntegration && isKeychainSupported()) {
114302
+ const r = addShellIntegration();
114303
+ if (r.success) {
114304
+ steps.push(`\u2713 Shell \u2192 ${r.path}`);
114305
+ } else {
114306
+ errors2.push(`Shell integration: ${r.error}`);
114307
+ }
114308
+ }
114309
+ const finalStatus = getKeychainStatus();
114310
+ const success = finalStatus.opTokenInKeychain && finalStatus.ageKeyInKeychain;
114311
+ if (outputJson) {
114312
+ ctx.output.data({ success, steps, errors: errors2, status: finalStatus });
114313
+ return;
114314
+ }
114315
+ for (const s of steps) {
114316
+ ctx.output.data(s);
114317
+ }
114318
+ if (success) {
114319
+ ctx.output.data("");
114320
+ ctx.output.data("\u2713 Done! Run: source ~/.zshrc");
114321
+ return;
114322
+ }
114323
+ ctx.output.data("");
114324
+ ctx.output.data("\u2500".repeat(50));
114325
+ ctx.output.data("Could not complete automatic setup. Manual steps:");
114326
+ ctx.output.data("");
114327
+ if (!opToken) {
114328
+ ctx.output.data("1. Get OP_SERVICE_ACCOUNT_TOKEN:");
114329
+ ctx.output.data(` open "${OP_VAULT_LINK}"`);
114330
+ ctx.output.data(' Copy the "credential" field');
114331
+ ctx.output.data("");
114332
+ ctx.output.data("2. Add to ~/.zshrc:");
114333
+ ctx.output.data(' export OP_SERVICE_ACCOUNT_TOKEN="<paste>"');
114334
+ ctx.output.data("");
114335
+ }
114336
+ if (!ageKey) {
114337
+ ctx.output.data("3. Get age key (requires OP token):");
114338
+ ctx.output.data(
114339
+ ' op read "op://Support/skill-cli-age-key/private_key"'
114340
+ );
114341
+ ctx.output.data("");
114342
+ ctx.output.data("4. Add to ~/.zshrc:");
114343
+ ctx.output.data(' export SKILL_AGE_KEY="<paste>"');
114344
+ ctx.output.data("");
114345
+ }
114346
+ if (!isKeychainSupported()) {
114347
+ ctx.output.data(
114348
+ "Note: No keychain on this platform. Use env vars instead."
114349
+ );
114350
+ }
114351
+ ctx.output.data("Then run: source ~/.zshrc");
114352
+ });
114097
114353
  }
114098
114354
 
114099
114355
  // src/commands/linear/index.ts
114100
114356
  init_esm_shims();
114101
114357
 
114102
- // src/commands/linear/assign.ts
114103
- init_esm_shims();
114104
-
114105
114358
  // src/core/write-gate.ts
114106
114359
  init_esm_shims();
114107
114360
  function requirePersonalKey(keyName) {
@@ -114117,6 +114370,9 @@ function requirePersonalKey(keyName) {
114117
114370
  });
114118
114371
  }
114119
114372
 
114373
+ // src/commands/linear/assign.ts
114374
+ init_esm_shims();
114375
+
114120
114376
  // src/commands/linear/client.ts
114121
114377
  init_esm_shims();
114122
114378
  import { LinearClient } from "@linear/sdk";
@@ -115561,26 +115817,31 @@ async function listMyIssues(ctx, options = {}) {
115561
115817
  const limit2 = options.limit || 20;
115562
115818
  try {
115563
115819
  const client = getLinearClient();
115564
- const viewer = await client.viewer;
115565
- const filter4 = {
115566
- assignee: { id: { eq: viewer.id } },
115567
- state: {
115568
- type: {
115569
- nin: ["canceled", "completed"]
115820
+ const { viewer, issues } = await withSpinner(
115821
+ "Loading issues...",
115822
+ async () => {
115823
+ const viewer2 = await client.viewer;
115824
+ const filter4 = {
115825
+ assignee: { id: { eq: viewer2.id } },
115826
+ state: {
115827
+ type: {
115828
+ nin: ["canceled", "completed"]
115829
+ }
115830
+ }
115831
+ };
115832
+ if (options.state) {
115833
+ filter4.state = {
115834
+ ...filter4.state || {},
115835
+ name: { eqIgnoreCase: options.state }
115836
+ };
115570
115837
  }
115838
+ const response = await client.issues({
115839
+ first: limit2,
115840
+ filter: filter4
115841
+ });
115842
+ return { viewer: viewer2, issues: response.nodes || [] };
115571
115843
  }
115572
- };
115573
- if (options.state) {
115574
- filter4.state = {
115575
- ...filter4.state || {},
115576
- name: { eqIgnoreCase: options.state }
115577
- };
115578
- }
115579
- const response = await client.issues({
115580
- first: limit2,
115581
- filter: filter4
115582
- });
115583
- const issues = response.nodes || [];
115844
+ );
115584
115845
  const issuesWithDetails = await Promise.all(
115585
115846
  issues.map(async (issue) => ({
115586
115847
  issue,
@@ -115759,9 +116020,12 @@ async function searchIssues(ctx, query, options = {}) {
115759
116020
  const limit2 = options.limit || 20;
115760
116021
  try {
115761
116022
  const client = getLinearClient();
115762
- const response = await client.searchIssues(query.trim(), {
115763
- first: limit2
115764
- });
116023
+ const response = await withSpinner(
116024
+ "Searching issues...",
116025
+ () => client.searchIssues(query.trim(), {
116026
+ first: limit2
116027
+ })
116028
+ );
115765
116029
  const issues = response.nodes || [];
115766
116030
  const issuesWithDetails = await Promise.all(
115767
116031
  issues.map(async (issue) => ({
@@ -116050,7 +116314,7 @@ init_esm_shims();
116050
116314
  async function listTeams(ctx, options = {}) {
116051
116315
  try {
116052
116316
  const client = getLinearClient();
116053
- const response = await client.teams();
116317
+ const response = await withSpinner("Loading teams...", () => client.teams());
116054
116318
  const teams = response.nodes || [];
116055
116319
  if (ctx.format === "json") {
116056
116320
  ctx.output.data(
@@ -116318,7 +116582,9 @@ Quick start:
116318
116582
  skill linear create "Title" Create issue
116319
116583
  skill linear search "query" Search issues
116320
116584
 
116321
- All commands support --json for machine-readable output.`
116585
+ All commands support --json for machine-readable output.
116586
+
116587
+ \u26A0\uFE0F Write operations require personal LINEAR_API_KEY (run 'skill keys add').`
116322
116588
  );
116323
116589
  linear.command("issues").description(
116324
116590
  `List issues with optional filters
@@ -116348,6 +116614,7 @@ Examples:
116348
116614
  skill linear my --state "In Progress" Only in-progress
116349
116615
  skill linear my --limit 5 Just top 5`
116350
116616
  ).option("--limit <number>", "Maximum results (default: 20)", "20").option("--state <name>", "Filter by state name").option("--json", "Output as JSON with HATEOAS links").action(async (options, command) => {
116617
+ requirePersonalKey("LINEAR_API_KEY");
116351
116618
  const ctx = await contextFromCommand2(command, options);
116352
116619
  await listMyIssues(ctx, {
116353
116620
  limit: parseInt(options.limit || "20", 10),
@@ -116392,6 +116659,7 @@ Priority: 0=Urgent, 1=High, 2=Medium, 3=Low, 4=None`
116392
116659
  (v, p) => [...p, v],
116393
116660
  []
116394
116661
  ).option("--project <name>", "Project name").option("--estimate <points>", "Estimate in points").option("--due-date <YYYY-MM-DD>", "Due date").option("--json", "Output as JSON with HATEOAS links").action(async (title, options, command) => {
116662
+ requirePersonalKey("LINEAR_API_KEY");
116395
116663
  const ctx = await contextFromCommand2(command, options);
116396
116664
  await createIssue(ctx, title, {
116397
116665
  description: options.description,
@@ -116412,6 +116680,7 @@ Examples:
116412
116680
  skill linear update ENG-123 --priority 1 --estimate 3
116413
116681
  skill linear update ENG-123 --due-date 2024-03-15`
116414
116682
  ).argument("<id>", "Issue identifier").option("--title <text>", "New title").option("--description <text>", "New description").option("--priority <0-4>", "New priority").option("--estimate <points>", "New estimate").option("--due-date <YYYY-MM-DD>", "New due date").option("--project <name>", "Move to project").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116683
+ requirePersonalKey("LINEAR_API_KEY");
116415
116684
  const ctx = await contextFromCommand2(command, options);
116416
116685
  await updateIssue(ctx, id, {
116417
116686
  title: options.title,
@@ -116430,6 +116699,7 @@ Examples:
116430
116699
  skill linear assign ENG-123 --to me
116431
116700
  skill linear assign ENG-123 --unassign`
116432
116701
  ).argument("<id>", "Issue identifier").option("--to <email>", 'Assign to user email (or "me")').option("--unassign", "Remove assignee").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116702
+ requirePersonalKey("LINEAR_API_KEY");
116433
116703
  const ctx = await contextFromCommand2(command, options);
116434
116704
  await assignIssue(ctx, id, {
116435
116705
  to: options.to,
@@ -116445,6 +116715,7 @@ Examples:
116445
116715
 
116446
116716
  Use 'skill linear states <team>' to see available states.`
116447
116717
  ).argument("<id>", "Issue identifier").requiredOption("--state <name>", "Target state name").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116718
+ requirePersonalKey("LINEAR_API_KEY");
116448
116719
  const ctx = await contextFromCommand2(command, options);
116449
116720
  await changeState(ctx, id, { state: options.state });
116450
116721
  });
@@ -116455,6 +116726,7 @@ Examples:
116455
116726
  skill linear close ENG-123 Close as done
116456
116727
  skill linear close ENG-123 --canceled Cancel the issue`
116457
116728
  ).argument("<id>", "Issue identifier").option("--canceled", "Close as canceled instead of done").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116729
+ requirePersonalKey("LINEAR_API_KEY");
116458
116730
  const ctx = await contextFromCommand2(command, options);
116459
116731
  await closeIssue(ctx, id, { canceled: options.canceled });
116460
116732
  });
@@ -116478,6 +116750,7 @@ Use 'skill linear labels <team>' to see available labels.`
116478
116750
  (v, p) => [...p, v],
116479
116751
  []
116480
116752
  ).option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116753
+ requirePersonalKey("LINEAR_API_KEY");
116481
116754
  const ctx = await contextFromCommand2(command, options);
116482
116755
  await modifyLabels(ctx, id, {
116483
116756
  add: options.add,
@@ -116493,6 +116766,7 @@ Examples:
116493
116766
  skill linear link ENG-123 --related ENG-456
116494
116767
  skill linear link ENG-123 --duplicate ENG-456`
116495
116768
  ).argument("<id>", "Source issue identifier").option("--blocks <id>", "This issue blocks <id>").option("--blocked-by <id>", "This issue is blocked by <id>").option("--related <id>", "Related to <id>").option("--duplicate <id>", "Duplicate of <id>").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116769
+ requirePersonalKey("LINEAR_API_KEY");
116496
116770
  const ctx = await contextFromCommand2(command, options);
116497
116771
  await linkIssues(ctx, id, {
116498
116772
  blocks: options.blocks,
@@ -116508,6 +116782,7 @@ Examples:
116508
116782
  skill linear comment ENG-123 --body "Great work!"
116509
116783
  skill linear comment ENG-123 --body "## Update\\n- Item 1\\n- Item 2"`
116510
116784
  ).argument("<id>", "Issue identifier").requiredOption("--body <text>", "Comment text (supports markdown)").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116785
+ requirePersonalKey("LINEAR_API_KEY");
116511
116786
  const ctx = await contextFromCommand2(command, options);
116512
116787
  await addComment(ctx, id, { body: options.body });
116513
116788
  });
@@ -117593,7 +117868,7 @@ var handlePipelineError = (ctx, error, message, suggestion = "Verify inputs and
117593
117868
  async function runPipelineCommand(ctx, opts) {
117594
117869
  const outputJson = opts.json === true || ctx.format === "json";
117595
117870
  try {
117596
- const { runPipeline: runPipeline2 } = await import("./pipeline-FGI6ICWM.js");
117871
+ const { runPipeline: runPipeline2 } = await import("./pipeline-TMFQSA7X.js");
117597
117872
  const result = await runPipeline2({
117598
117873
  message: {
117599
117874
  subject: opts.subject,
@@ -119209,9 +119484,6 @@ init_esm_shims();
119209
119484
  var DEFAULT_MAX_HINTS = 2;
119210
119485
  var getCommandCount = (state, command) => state.commands[command]?.count ?? 0;
119211
119486
  var hasCommand = (state, command) => getCommandCount(state, command) > 0;
119212
- var hasCommandPrefix = (state, prefix) => Object.entries(state.commands).some(
119213
- ([name, entry]) => name.startsWith(prefix) && entry.count > 0
119214
- );
119215
119487
  var hasMilestone = (state, milestone) => state.milestones[milestone]?.achieved ?? false;
119216
119488
  var shouldSuppressHints = (context) => context.quiet === true || context.format === "json";
119217
119489
  var resolveMaxHints = (context) => context.maxHints ?? DEFAULT_MAX_HINTS;
@@ -119220,79 +119492,76 @@ var toHint = (rule) => ({
119220
119492
  message: rule.message,
119221
119493
  audience: rule.audience
119222
119494
  });
119495
+ var isCommandGroup = (command, group) => command === group || command.startsWith(`${group}.`);
119223
119496
  var DEFAULT_HINT_RULES = [
119497
+ // Front contextual hints
119224
119498
  {
119225
- id: "onboarding.wizard",
119226
- audience: "onboarding",
119227
- message: "New here? Run `skill wizard` to set up your first product.",
119228
- showWhen: (state) => state.totalRuns <= 2 && !hasCommand(state, "wizard"),
119229
- retireWhen: (state) => hasCommand(state, "wizard") || hasMilestone(state, "wizard_completed")
119230
- },
119231
- {
119232
- id: "onboarding.auth",
119233
- audience: "onboarding",
119234
- message: "Set up your own API keys with `skill keys`.",
119235
- showWhen: (state) => state.totalRuns >= 1 && !hasMilestone(state, "auth_configured"),
119236
- retireWhen: (state) => hasMilestone(state, "auth_configured")
119237
- },
119238
- {
119239
- id: "discovery.health",
119240
- audience: "discovery",
119241
- message: "Check integrations fast with `skill health <app-slug>`.",
119242
- showWhen: (state) => state.totalRuns >= 2 && !hasCommand(state, "health"),
119243
- retireWhen: (state) => hasCommand(state, "health")
119499
+ id: "context.front.triage",
119500
+ audience: "contextual",
119501
+ postRun: true,
119502
+ message: "Tip: `skill front triage` to auto-categorize unassigned threads.",
119503
+ showWhen: (state, context) => context.command === "front.inbox" && !hasCommand(state, "front.triage"),
119504
+ retireWhen: (state) => hasCommand(state, "front.triage")
119244
119505
  },
119245
119506
  {
119246
- id: "discovery.front.inbox",
119247
- audience: "discovery",
119248
- message: "List recent conversations via `skill front inbox <name-or-id>`.",
119249
- showWhen: (state) => state.totalRuns >= 1 && !hasCommand(state, "front.inbox"),
119250
- retireWhen: (state) => hasCommand(state, "front.inbox")
119507
+ id: "context.front.conversation",
119508
+ audience: "contextual",
119509
+ postRun: true,
119510
+ message: "Tip: `skill front conversation <id> -m` shows the full thread.",
119511
+ showWhen: (state, context) => context.command === "front.message" && !hasCommand(state, "front.conversation"),
119512
+ retireWhen: (state) => hasCommand(state, "front.conversation")
119251
119513
  },
119252
119514
  {
119253
- id: "discovery.inngest",
119254
- audience: "discovery",
119255
- message: "Inspect workflows with `skill inngest stats --after 1d`.",
119256
- showWhen: (state) => state.totalRuns >= 3 && !hasCommandPrefix(state, "inngest."),
119257
- retireWhen: (state) => hasCommandPrefix(state, "inngest.")
119515
+ id: "context.front.reply",
119516
+ audience: "contextual",
119517
+ postRun: true,
119518
+ message: "Tip: `skill front reply <id>` to draft a response.",
119519
+ showWhen: (state, context) => context.command === "front.conversation" && !hasCommand(state, "front.reply"),
119520
+ retireWhen: (state) => hasCommand(state, "front.reply")
119258
119521
  },
119522
+ // Inngest contextual hints
119259
119523
  {
119260
- id: "discovery.axiom",
119261
- audience: "discovery",
119262
- message: 'Query logs quickly with `skill axiom query "<APL>" --since 24h`.',
119263
- showWhen: (state) => state.totalRuns >= 3 && !hasCommandPrefix(state, "axiom."),
119264
- retireWhen: (state) => hasCommandPrefix(state, "axiom.")
119524
+ id: "context.inngest.run",
119525
+ audience: "contextual",
119526
+ postRun: true,
119527
+ message: "Tip: `skill inngest run <id>` to inspect a specific run.",
119528
+ showWhen: (state, context) => (context.command === "inngest.failures" || context.command === "inngest.runs") && !hasCommand(state, "inngest.run"),
119529
+ retireWhen: (state) => hasCommand(state, "inngest.run")
119265
119530
  },
119266
119531
  {
119267
- id: "discovery.keys",
119268
- audience: "discovery",
119269
- message: "Override shared credentials with your own: `skill keys add`",
119270
- showWhen: (state) => state.totalRuns >= 3 && !hasCommand(state, "keys") && !hasMilestone(state, "auth_configured"),
119271
- retireWhen: (state) => hasCommand(state, "keys") || hasCommand(state, "keys.add") || hasMilestone(state, "auth_configured")
119532
+ id: "context.inngest.trace",
119533
+ audience: "contextual",
119534
+ postRun: true,
119535
+ message: "Tip: `skill inngest trace <run-id>` for full workflow trace.",
119536
+ showWhen: (state, context) => context.command === "inngest.run" && !hasCommand(state, "inngest.trace"),
119537
+ retireWhen: (state) => hasCommand(state, "inngest.trace")
119272
119538
  },
119539
+ // Linear contextual hints
119273
119540
  {
119274
- id: "context.front.triage",
119541
+ id: "context.linear.my",
119275
119542
  audience: "contextual",
119276
119543
  postRun: true,
119277
- message: "Tip: `skill front triage <inbox-id>` surfaces unassigned threads.",
119278
- showWhen: (state, context) => context.command === "front.inbox" && !hasCommand(state, "front.triage"),
119279
- retireWhen: (state) => hasCommand(state, "front.triage")
119544
+ message: "Tip: `skill linear my` to see your assigned issues.",
119545
+ showWhen: (state, context) => isCommandGroup(context.command, "linear") && context.command !== "linear.my" && !hasCommand(state, "linear.my"),
119546
+ retireWhen: (state) => hasCommand(state, "linear.my")
119280
119547
  },
119548
+ // Axiom contextual hints
119281
119549
  {
119282
- id: "context.front.conversation",
119550
+ id: "context.axiom.errors",
119283
119551
  audience: "contextual",
119284
119552
  postRun: true,
119285
- message: "Tip: `skill front conversation <id> -m` shows the full thread.",
119286
- showWhen: (state, context) => context.command === "front.message" && !hasCommand(state, "front.conversation"),
119287
- retireWhen: (state) => hasCommand(state, "front.conversation")
119553
+ message: "Tip: `skill axiom errors --since 1h` to see recent errors.",
119554
+ showWhen: (state, context) => context.command === "axiom.query" && !hasCommand(state, "axiom.errors"),
119555
+ retireWhen: (state) => hasCommand(state, "axiom.errors")
119288
119556
  },
119557
+ // Keys hint - only when auth issues are likely
119289
119558
  {
119290
- id: "context.inngest.run",
119559
+ id: "context.keys",
119291
119560
  audience: "contextual",
119292
119561
  postRun: true,
119293
- message: "Tip: drill in with `skill inngest run <id>` or `skill inngest trace <run-id>`.",
119294
- showWhen: (state, context) => context.command === "inngest.failures" && !hasCommand(state, "inngest.run") && !hasCommand(state, "inngest.trace"),
119295
- retireWhen: (state) => hasCommand(state, "inngest.run") || hasCommand(state, "inngest.trace")
119562
+ message: "Tip: `skill keys setup` to configure keychain integration.",
119563
+ showWhen: (state, context) => context.command === "auth.status" && !hasCommand(state, "keys.setup") && !hasMilestone(state, "auth_configured"),
119564
+ retireWhen: (state) => hasCommand(state, "keys.setup") || hasMilestone(state, "auth_configured")
119296
119565
  }
119297
119566
  ];
119298
119567
  var HintEngine = class {
@@ -120076,8 +120345,8 @@ if (!envLoaded && !process.env.DATABASE_URL) {
120076
120345
  process.env.SKIP_ENV_VALIDATION = "1";
120077
120346
  }
120078
120347
  var runtimeTarget = `bun-${process.platform}-${process.arch}`;
120079
- var buildVersion = "0.16.0".length > 0 ? "0.16.0" : "0.0.0-dev";
120080
- var buildCommit = "d96a5e3".length > 0 ? "d96a5e3" : "dev";
120348
+ var buildVersion = "0.17.0".length > 0 ? "0.17.0" : "0.0.0-dev";
120349
+ var buildCommit = "e664abd".length > 0 ? "e664abd" : "dev";
120081
120350
  var buildTarget = "node".length > 0 ? "node" : runtimeTarget;
120082
120351
  var isDevBuild = buildVersion.includes("dev") || buildCommit === "dev";
120083
120352
  var versionLabel = `skill v${buildVersion} (${buildCommit}) ${buildTarget}`;
@@ -120091,7 +120360,6 @@ var usageState = await (async () => {
120091
120360
  return null;
120092
120361
  }
120093
120362
  })();
120094
- var hintCounts = /* @__PURE__ */ new WeakMap();
120095
120363
  var commandStartTimes = /* @__PURE__ */ new WeakMap();
120096
120364
  var resolveCommandName = (command) => {
120097
120365
  const names = [];
@@ -120147,16 +120415,6 @@ program2.hook("preAction", (thisCommand, actionCommand) => {
120147
120415
  program2.hook("preAction", (_thisCommand, actionCommand) => {
120148
120416
  commandStartTimes.set(actionCommand, Date.now());
120149
120417
  });
120150
- program2.hook("preAction", async (_thisCommand, actionCommand) => {
120151
- try {
120152
- const context = resolveHintContext(actionCommand);
120153
- const state = await usageTracker.getUsage();
120154
- const hints = hintEngine.getHints(state, context);
120155
- writeHints(hints, process.stderr);
120156
- hintCounts.set(actionCommand, hints.length);
120157
- } catch {
120158
- }
120159
- });
120160
120418
  program2.hook("postAction", async (_thisCommand, actionCommand) => {
120161
120419
  try {
120162
120420
  const context = resolveHintContext(actionCommand);
@@ -120165,11 +120423,7 @@ program2.hook("postAction", async (_thisCommand, actionCommand) => {
120165
120423
  for (const milestone of milestones) {
120166
120424
  await usageTracker.setMilestone(milestone);
120167
120425
  }
120168
- const previouslyShown = hintCounts.get(actionCommand) ?? 0;
120169
- const postHint = hintEngine.getPostRunHint(state, {
120170
- ...context,
120171
- previouslyShown
120172
- });
120426
+ const postHint = hintEngine.getPostRunHint(state, context);
120173
120427
  if (postHint) writeHints([postHint], process.stderr);
120174
120428
  } catch {
120175
120429
  }