@skillrecordings/cli 0.16.1 → 0.18.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,28 @@ 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
+ getFromKeychain,
60
+ getKeychainStatus,
61
+ isKeychainSupported,
62
+ isOpCliAvailable,
63
+ storeInKeychain
64
+ } from "./chunk-7SQU7KCI.js";
56
65
  import {
57
66
  OnePasswordProvider,
58
67
  SECRET_REFS,
@@ -74611,9 +74620,9 @@ function parseEnvContent(content) {
74611
74620
  return env;
74612
74621
  }
74613
74622
  async function decryptEnvFile(encryptedPath) {
74614
- const { existsSync: existsSync15 } = await import("fs");
74623
+ const { existsSync: existsSync17 } = await import("fs");
74615
74624
  const { readFile: readFile10 } = await import("fs/promises");
74616
- if (!existsSync15(encryptedPath)) {
74625
+ if (!existsSync17(encryptedPath)) {
74617
74626
  return {};
74618
74627
  }
74619
74628
  const ageKey = await getAgeKeyFrom1Password();
@@ -74630,19 +74639,43 @@ async function decryptEnvFile(encryptedPath) {
74630
74639
  }
74631
74640
  }
74632
74641
  async function getAgeKeyFrom1Password() {
74633
- if (!process.env.OP_SERVICE_ACCOUNT_TOKEN) {
74634
- return null;
74642
+ if (process.env.SKILL_AGE_KEY) {
74643
+ return process.env.SKILL_AGE_KEY;
74635
74644
  }
74636
74645
  try {
74637
- const { OnePasswordProvider: OnePasswordProvider2 } = await import("./secrets-MGVPGMFJ.js");
74638
- const op = new OnePasswordProvider2();
74639
- if (!await op.isAvailable()) {
74640
- return null;
74646
+ const { getFromKeychain: getFromKeychain2, storeInKeychain: storeInKeychain2, autoBootstrapKeychain: autoBootstrapKeychain2 } = await import("./keychain-IEZHT5WN.js");
74647
+ const fromKeychain = getFromKeychain2("age-private-key");
74648
+ if (fromKeychain) return fromKeychain;
74649
+ let opToken = process.env.OP_SERVICE_ACCOUNT_TOKEN;
74650
+ if (!opToken) {
74651
+ opToken = autoBootstrapKeychain2() ?? void 0;
74652
+ }
74653
+ if (opToken) {
74654
+ const originalEnv = process.env.OP_SERVICE_ACCOUNT_TOKEN;
74655
+ process.env.OP_SERVICE_ACCOUNT_TOKEN = opToken;
74656
+ try {
74657
+ const { OnePasswordProvider: OnePasswordProvider2 } = await import("./secrets-MGVPGMFJ.js");
74658
+ const op = new OnePasswordProvider2();
74659
+ if (await op.isAvailable()) {
74660
+ const key = await op.resolve(
74661
+ "op://Support/skill-cli-age-key/private_key"
74662
+ );
74663
+ if (key) {
74664
+ storeInKeychain2("age-private-key", key);
74665
+ return key;
74666
+ }
74667
+ }
74668
+ } finally {
74669
+ if (originalEnv) {
74670
+ process.env.OP_SERVICE_ACCOUNT_TOKEN = originalEnv;
74671
+ } else {
74672
+ delete process.env.OP_SERVICE_ACCOUNT_TOKEN;
74673
+ }
74674
+ }
74641
74675
  }
74642
- return await op.resolve("op://Support/skill-cli-age-key/private_key");
74643
74676
  } catch {
74644
- return null;
74645
74677
  }
74678
+ return null;
74646
74679
  }
74647
74680
  async function loadShippedDefaults(cliRoot2) {
74648
74681
  const encryptedPath = resolve(cliRoot2, ".env.encrypted");
@@ -74719,7 +74752,7 @@ var ROOT_DESCRIPTIONS = {
74719
74752
  minimal: "Skill Recordings support agent CLI. Try: skill wizard, skill keys, skill front inbox, skill front triage. Use --help for details."
74720
74753
  };
74721
74754
  var FRONT_DESCRIPTIONS = {
74722
- 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.",
74755
+ 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.\n\n Environment: FRONT_API_TOKEN required. Run `skill doctor` to check.",
74723
74756
  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>",
74724
74757
  minimal: "Front API commands (inbox, triage, assign, reply, archive)."
74725
74758
  };
@@ -74729,7 +74762,7 @@ var AUTH_DESCRIPTIONS = {
74729
74762
  minimal: "Auth status commands (auth status, auth whoami)."
74730
74763
  };
74731
74764
  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",
74765
+ 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 Environment: INNGEST_SIGNING_KEY required. Run `skill doctor` to check.",
74733
74766
  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>",
74734
74767
  minimal: "Inngest events and runs debugging."
74735
74768
  };
@@ -75667,6 +75700,74 @@ function registerAuthCommands(program3, usageState2) {
75667
75700
  // src/commands/axiom/index.ts
75668
75701
  init_esm_shims();
75669
75702
 
75703
+ // src/core/spinner.ts
75704
+ init_esm_shims();
75705
+ var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
75706
+ var INTERVAL = 80;
75707
+ function createSpinner() {
75708
+ const isTTY = process.stderr.isTTY && !process.env.CI;
75709
+ let interval = null;
75710
+ let frameIndex = 0;
75711
+ let currentMessage = "";
75712
+ const clear = () => {
75713
+ if (!isTTY) return;
75714
+ process.stderr.write("\r\x1B[K");
75715
+ };
75716
+ const render3 = () => {
75717
+ if (!isTTY) return;
75718
+ const frame = FRAMES[frameIndex];
75719
+ process.stderr.write(`\r${frame} ${currentMessage}`);
75720
+ frameIndex = (frameIndex + 1) % FRAMES.length;
75721
+ };
75722
+ const start = (message = "Loading...") => {
75723
+ if (!isTTY) return;
75724
+ currentMessage = message;
75725
+ frameIndex = 0;
75726
+ render3();
75727
+ interval = setInterval(render3, INTERVAL);
75728
+ };
75729
+ const update2 = (message) => {
75730
+ currentMessage = message;
75731
+ if (isTTY && !interval) {
75732
+ render3();
75733
+ }
75734
+ };
75735
+ const stop = () => {
75736
+ if (interval) {
75737
+ clearInterval(interval);
75738
+ interval = null;
75739
+ }
75740
+ clear();
75741
+ };
75742
+ const succeed = (message) => {
75743
+ stop();
75744
+ if (isTTY && message) {
75745
+ process.stderr.write(`\u2713 ${message}
75746
+ `);
75747
+ }
75748
+ };
75749
+ const fail = (message) => {
75750
+ stop();
75751
+ if (isTTY && message) {
75752
+ process.stderr.write(`\u2717 ${message}
75753
+ `);
75754
+ }
75755
+ };
75756
+ return { start, update: update2, stop, succeed, fail };
75757
+ }
75758
+ async function withSpinner(message, fn) {
75759
+ const spinner = createSpinner();
75760
+ spinner.start(message);
75761
+ try {
75762
+ const result = await fn();
75763
+ spinner.stop();
75764
+ return result;
75765
+ } catch (error) {
75766
+ spinner.stop();
75767
+ throw error;
75768
+ }
75769
+ }
75770
+
75670
75771
  // src/lib/axiom-client.ts
75671
75772
  init_esm_shims();
75672
75773
  import { Axiom } from "@axiomhq/js";
@@ -76365,10 +76466,13 @@ async function runQuery(ctx, apl, options) {
76365
76466
  const { startTime, endTime } = parseTimeRange(options.since ?? "24h");
76366
76467
  const outputJson = options.json === true || ctx.format === "json";
76367
76468
  try {
76368
- const result = await client.query(apl, {
76369
- startTime: startTime.toISOString(),
76370
- endTime: endTime.toISOString()
76371
- });
76469
+ const result = await withSpinner(
76470
+ "Running query...",
76471
+ () => client.query(apl, {
76472
+ startTime: startTime.toISOString(),
76473
+ endTime: endTime.toISOString()
76474
+ })
76475
+ );
76372
76476
  if (outputJson) {
76373
76477
  ctx.output.data(result);
76374
76478
  return;
@@ -76412,10 +76516,13 @@ async function listAgentRuns(ctx, options) {
76412
76516
  | sort by _time desc
76413
76517
  | limit ${limit2}`;
76414
76518
  try {
76415
- const result = await client.query(apl, {
76416
- startTime: startTime.toISOString(),
76417
- endTime: endTime.toISOString()
76418
- });
76519
+ const result = await withSpinner(
76520
+ "Loading agent runs...",
76521
+ () => client.query(apl, {
76522
+ startTime: startTime.toISOString(),
76523
+ endTime: endTime.toISOString()
76524
+ })
76525
+ );
76419
76526
  const matches = result.matches ?? [];
76420
76527
  if (outputJson) {
76421
76528
  ctx.output.data(matches.map((m) => m.data));
@@ -76469,10 +76576,13 @@ async function listErrors(ctx, options) {
76469
76576
  | sort by _time desc
76470
76577
  | limit ${limit2}`;
76471
76578
  try {
76472
- const result = await client.query(apl, {
76473
- startTime: startTime.toISOString(),
76474
- endTime: endTime.toISOString()
76475
- });
76579
+ const result = await withSpinner(
76580
+ "Loading errors...",
76581
+ () => client.query(apl, {
76582
+ startTime: startTime.toISOString(),
76583
+ endTime: endTime.toISOString()
76584
+ })
76585
+ );
76476
76586
  const matches = result.matches ?? [];
76477
76587
  if (outputJson) {
76478
76588
  ctx.output.data(matches.map((m) => m.data));
@@ -76509,10 +76619,13 @@ async function getConversation(ctx, conversationId, options) {
76509
76619
  | where conversationId == '${conversationId}'
76510
76620
  | sort by _time asc`;
76511
76621
  try {
76512
- const result = await client.query(apl, {
76513
- startTime: startTime.toISOString(),
76514
- endTime: endTime.toISOString()
76515
- });
76622
+ const result = await withSpinner(
76623
+ "Loading conversation...",
76624
+ () => client.query(apl, {
76625
+ startTime: startTime.toISOString(),
76626
+ endTime: endTime.toISOString()
76627
+ })
76628
+ );
76516
76629
  const matches = result.matches ?? [];
76517
76630
  if (outputJson) {
76518
76631
  ctx.output.data(
@@ -76568,10 +76681,13 @@ async function getClassificationStats(ctx, options) {
76568
76681
  apl += `
76569
76682
  | summarize count = count() by category, complexity`;
76570
76683
  try {
76571
- const result = await client.query(apl, {
76572
- startTime: startTime.toISOString(),
76573
- endTime: endTime.toISOString()
76574
- });
76684
+ const result = await withSpinner(
76685
+ "Loading classifications...",
76686
+ () => client.query(apl, {
76687
+ startTime: startTime.toISOString(),
76688
+ endTime: endTime.toISOString()
76689
+ })
76690
+ );
76575
76691
  const buckets = result.buckets?.totals ?? [];
76576
76692
  if (outputJson) {
76577
76693
  ctx.output.data(buckets);
@@ -76631,10 +76747,13 @@ async function listWorkflowSteps(ctx, options) {
76631
76747
  | sort by _time desc
76632
76748
  | limit ${limit2}`;
76633
76749
  try {
76634
- const result = await client.query(apl, {
76635
- startTime: startTime.toISOString(),
76636
- endTime: endTime.toISOString()
76637
- });
76750
+ const result = await withSpinner(
76751
+ "Loading workflow steps...",
76752
+ () => client.query(apl, {
76753
+ startTime: startTime.toISOString(),
76754
+ endTime: endTime.toISOString()
76755
+ })
76756
+ );
76638
76757
  const matches = result.matches ?? [];
76639
76758
  if (outputJson) {
76640
76759
  ctx.output.data(matches.map((m) => m.data));
@@ -76677,10 +76796,13 @@ async function listApprovals(ctx, options) {
76677
76796
  | sort by _time desc
76678
76797
  | limit ${limit2}`;
76679
76798
  try {
76680
- const result = await client.query(apl, {
76681
- startTime: startTime.toISOString(),
76682
- endTime: endTime.toISOString()
76683
- });
76799
+ const result = await withSpinner(
76800
+ "Loading approvals...",
76801
+ () => client.query(apl, {
76802
+ startTime: startTime.toISOString(),
76803
+ endTime: endTime.toISOString()
76804
+ })
76805
+ );
76684
76806
  const matches = result.matches ?? [];
76685
76807
  if (outputJson) {
76686
76808
  ctx.output.data(matches.map((m) => m.data));
@@ -76716,7 +76838,9 @@ async function listApprovals(ctx, options) {
76716
76838
  }
76717
76839
  }
76718
76840
  function registerAxiomCommands(program3) {
76719
- const axiom = program3.command("axiom").description("Query Axiom logs and traces");
76841
+ const axiom = program3.command("axiom").description(
76842
+ "Query Axiom logs and traces.\n Environment: AXIOM_TOKEN required. Run `skill doctor` to check."
76843
+ );
76720
76844
  axiom.command("query").description("Run a raw APL query").argument("<apl>", "APL query string").option("-s, --since <time>", "Time range (e.g., 1h, 24h, 7d)", "24h").option("--json", "Output as JSON").action(async (apl, options, command) => {
76721
76845
  const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
76722
76846
  ...command.parent?.opts(),
@@ -76954,11 +77078,11 @@ Saved to ${options.output}`);
76954
77078
  }
76955
77079
  }
76956
77080
  async function toEvalite(options) {
76957
- const { readFileSync: readFileSync13 } = await import("fs");
77081
+ const { readFileSync: readFileSync14 } = await import("fs");
76958
77082
  const { ctx } = options;
76959
77083
  const outputJson = ctx.format === "json";
76960
77084
  const data2 = JSON.parse(
76961
- readFileSync13(options.input, "utf-8")
77085
+ readFileSync14(options.input, "utf-8")
76962
77086
  );
76963
77087
  const evaliteData = data2.map((d) => ({
76964
77088
  input: d.triggerMessage.body,
@@ -77762,6 +77886,170 @@ function registerDeployCommands(program3) {
77762
77886
  });
77763
77887
  }
77764
77888
 
77889
+ // src/commands/doctor.ts
77890
+ init_esm_shims();
77891
+ import { execSync as execSync3 } from "child_process";
77892
+ import { existsSync as existsSync6 } from "fs";
77893
+ import { join as join3 } from "path";
77894
+ var REQUIRED_ENV_VARS = [
77895
+ "DATABASE_URL",
77896
+ "INNGEST_SIGNING_KEY",
77897
+ "INNGEST_EVENT_KEY",
77898
+ "FRONT_API_TOKEN",
77899
+ "LINEAR_API_KEY",
77900
+ "AXIOM_TOKEN",
77901
+ "SLACK_BOT_TOKEN",
77902
+ "UPSTASH_REDIS_REST_URL",
77903
+ "UPSTASH_VECTOR_REST_URL"
77904
+ ];
77905
+ function checkEnvVars() {
77906
+ return REQUIRED_ENV_VARS.map((varName) => {
77907
+ const value = process.env[varName];
77908
+ return {
77909
+ name: varName,
77910
+ status: value ? "ok" : "warn",
77911
+ message: value ? void 0 : "Not set"
77912
+ };
77913
+ });
77914
+ }
77915
+ function checkKeychain() {
77916
+ const checks = [];
77917
+ const opAvailable = isOpCliAvailable();
77918
+ checks.push({
77919
+ name: "op CLI",
77920
+ status: opAvailable ? "ok" : "warn",
77921
+ message: opAvailable ? void 0 : "Not installed or not authenticated"
77922
+ });
77923
+ const opToken = getFromKeychain("op-service-account-token");
77924
+ checks.push({
77925
+ name: "op-service-account-token",
77926
+ status: opToken ? "ok" : "warn",
77927
+ message: opToken ? void 0 : "Not found in keychain"
77928
+ });
77929
+ const ageKey = getFromKeychain("age-private-key");
77930
+ checks.push({
77931
+ name: "age-private-key",
77932
+ status: ageKey ? "ok" : "warn",
77933
+ message: ageKey ? void 0 : "Not found in keychain"
77934
+ });
77935
+ return checks;
77936
+ }
77937
+ function checkTools() {
77938
+ const checks = [];
77939
+ try {
77940
+ execSync3("gh auth status", { stdio: "pipe", timeout: 3e3 });
77941
+ checks.push({
77942
+ name: "gh CLI",
77943
+ status: "ok"
77944
+ });
77945
+ } catch {
77946
+ checks.push({
77947
+ name: "gh CLI",
77948
+ status: "warn",
77949
+ message: "Not installed or not authenticated"
77950
+ });
77951
+ }
77952
+ return checks;
77953
+ }
77954
+ function checkWorkspace() {
77955
+ const checks = [];
77956
+ const hivePath = join3(process.cwd(), ".hive");
77957
+ const hiveExists = existsSync6(hivePath);
77958
+ checks.push({
77959
+ name: ".hive directory",
77960
+ status: hiveExists ? "ok" : "warn",
77961
+ message: hiveExists ? void 0 : "Not found in current directory"
77962
+ });
77963
+ return checks;
77964
+ }
77965
+ function runHealthChecks() {
77966
+ const categories = [
77967
+ {
77968
+ category: "Environment",
77969
+ checks: checkEnvVars()
77970
+ },
77971
+ {
77972
+ category: "Keychain",
77973
+ checks: checkKeychain()
77974
+ },
77975
+ {
77976
+ category: "Tools",
77977
+ checks: checkTools()
77978
+ },
77979
+ {
77980
+ category: "Workspace",
77981
+ checks: checkWorkspace()
77982
+ }
77983
+ ];
77984
+ let total = 0;
77985
+ let ok = 0;
77986
+ let warn = 0;
77987
+ let fail = 0;
77988
+ for (const category of categories) {
77989
+ for (const check of category.checks) {
77990
+ total++;
77991
+ if (check.status === "ok") ok++;
77992
+ else if (check.status === "warn") warn++;
77993
+ else fail++;
77994
+ }
77995
+ }
77996
+ let status;
77997
+ if (fail > 0) {
77998
+ status = "unhealthy";
77999
+ } else if (warn > 3) {
78000
+ status = "degraded";
78001
+ } else {
78002
+ status = "healthy";
78003
+ }
78004
+ return {
78005
+ status,
78006
+ categories,
78007
+ summary: { total, ok, warn, fail }
78008
+ };
78009
+ }
78010
+ function formatTextOutput(results) {
78011
+ console.log("\n\u{1FA7A} Health Check Results\n");
78012
+ for (const category of results.categories) {
78013
+ console.log(`${category.category}:`);
78014
+ for (const check of category.checks) {
78015
+ const symbol = check.status === "ok" ? "\u2713" : check.status === "warn" ? "\u26A0" : "\u2717";
78016
+ const line = check.message ? ` ${symbol} ${check.name} - ${check.message}` : ` ${symbol} ${check.name}`;
78017
+ console.log(line);
78018
+ }
78019
+ console.log("");
78020
+ }
78021
+ console.log("\u2500".repeat(60));
78022
+ console.log(
78023
+ `Summary: ${results.summary.ok}/${results.summary.total} checks passed`
78024
+ );
78025
+ if (results.summary.warn > 0) {
78026
+ console.log(`\u26A0 ${results.summary.warn} warnings`);
78027
+ }
78028
+ if (results.summary.fail > 0) {
78029
+ console.log(`\u2717 ${results.summary.fail} failures`);
78030
+ }
78031
+ console.log(`
78032
+ Overall status: ${results.status.toUpperCase()}`);
78033
+ console.log("");
78034
+ }
78035
+ function registerDoctorCommand(program3) {
78036
+ program3.command("doctor").description(
78037
+ "Run health checks on environment and tools\n\n Checks:\n - Environment variables\n - Keychain secrets\n - CLI tools (gh, op)\n - Workspace setup (.hive)\n\n Examples:\n skill doctor\n skill doctor --json"
78038
+ ).option("--json", "Output as JSON").action(async (options, command) => {
78039
+ const ctx = await createContext({
78040
+ format: options.json ? "json" : "text",
78041
+ verbose: command.optsWithGlobals().verbose,
78042
+ quiet: command.optsWithGlobals().quiet
78043
+ });
78044
+ const results = runHealthChecks();
78045
+ if (options.json || ctx.format === "json") {
78046
+ ctx.output.data(results);
78047
+ } else {
78048
+ formatTextOutput(results);
78049
+ }
78050
+ });
78051
+ }
78052
+
77765
78053
  // src/commands/eval.ts
77766
78054
  init_esm_shims();
77767
78055
  import { access, readFile as readFile2 } from "fs/promises";
@@ -79769,12 +80057,12 @@ async function scoreProduction(ctx, options) {
79769
80057
 
79770
80058
  // src/commands/eval-local/seed.ts
79771
80059
  init_esm_shims();
79772
- import { join as join4 } from "path";
80060
+ import { join as join5 } from "path";
79773
80061
  import { glob as glob3 } from "glob";
79774
80062
 
79775
80063
  // src/lib/eval-seed.ts
79776
80064
  init_esm_shims();
79777
- import { join as join3 } from "path";
80065
+ import { join as join4 } from "path";
79778
80066
  import { readFile as readFile5, readdir } from "fs/promises";
79779
80067
  import { glob as glob2 } from "glob";
79780
80068
  import matter from "gray-matter";
@@ -79820,7 +80108,7 @@ async function loadJsonFiles(dirPath) {
79820
80108
  const jsonFiles = files.filter((f) => f.endsWith(".json"));
79821
80109
  const items = await Promise.all(
79822
80110
  jsonFiles.map(async (file) => {
79823
- const content = await readFile5(join3(dirPath, file), "utf-8");
80111
+ const content = await readFile5(join4(dirPath, file), "utf-8");
79824
80112
  return JSON.parse(content);
79825
80113
  })
79826
80114
  );
@@ -79830,7 +80118,7 @@ async function loadJsonFiles(dirPath) {
79830
80118
  }
79831
80119
  }
79832
80120
  async function loadKnowledgeFiles(basePath) {
79833
- const files = await glob2(join3(basePath, "**/*.md"));
80121
+ const files = await glob2(join4(basePath, "**/*.md"));
79834
80122
  const docs = [];
79835
80123
  for (const filePath of files) {
79836
80124
  const content = await readFile5(filePath, "utf-8");
@@ -80020,16 +80308,16 @@ async function seed(ctx, options) {
80020
80308
  await cleanQdrant();
80021
80309
  }
80022
80310
  log("\u{1F4E6} Seeding apps...");
80023
- const apps = await loadJsonFiles(join4(fixturesPath, "apps"));
80311
+ const apps = await loadJsonFiles(join5(fixturesPath, "apps"));
80024
80312
  result.apps = await seedApps(connection, apps);
80025
80313
  log("\u{1F465} Loading customer fixtures...");
80026
- const customers = await loadJsonFiles(join4(fixturesPath, "customers"));
80314
+ const customers = await loadJsonFiles(join5(fixturesPath, "customers"));
80027
80315
  result.customers = customers.length;
80028
80316
  log("\u{1F4DA} Seeding knowledge base...");
80029
- const knowledge = await loadKnowledgeFiles(join4(fixturesPath, "knowledge"));
80317
+ const knowledge = await loadKnowledgeFiles(join5(fixturesPath, "knowledge"));
80030
80318
  result.knowledge = knowledge.length;
80031
80319
  result.embeddings = await seedKnowledgeBase(knowledge, !outputJson);
80032
- const scenarioFiles = await glob3(join4(fixturesPath, "scenarios/**/*.json"));
80320
+ const scenarioFiles = await glob3(join5(fixturesPath, "scenarios/**/*.json"));
80033
80321
  result.scenarios = scenarioFiles.length;
80034
80322
  await connection.end();
80035
80323
  if (outputJson) {
@@ -80105,8 +80393,8 @@ init_esm_shims();
80105
80393
  // src/commands/eval-pipeline/run.ts
80106
80394
  init_esm_shims();
80107
80395
  import { createHash as createHash2 } from "crypto";
80108
- import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync4 } from "fs";
80109
- import { join as join5 } from "path";
80396
+ import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync4 } from "fs";
80397
+ import { join as join6 } from "path";
80110
80398
  import { readFile as readFile6 } from "fs/promises";
80111
80399
  import { glob as glob4 } from "glob";
80112
80400
 
@@ -80519,11 +80807,11 @@ function getCacheKey(scenarioId, classifySourceHash) {
80519
80807
  function getClassifySourceHash() {
80520
80808
  try {
80521
80809
  const possiblePaths = [
80522
- join5(process.cwd(), "packages/core/src/pipeline/classify.ts"),
80523
- join5(process.cwd(), "../core/src/pipeline/classify.ts")
80810
+ join6(process.cwd(), "packages/core/src/pipeline/classify.ts"),
80811
+ join6(process.cwd(), "../core/src/pipeline/classify.ts")
80524
80812
  ];
80525
80813
  for (const path of possiblePaths) {
80526
- if (existsSync6(path)) {
80814
+ if (existsSync7(path)) {
80527
80815
  const content = readFileSync5(path, "utf-8");
80528
80816
  return createHash2("md5").update(content).digest("hex");
80529
80817
  }
@@ -80533,9 +80821,9 @@ function getClassifySourceHash() {
80533
80821
  return createHash2("md5").update(Math.floor(Date.now() / 3e5).toString()).digest("hex");
80534
80822
  }
80535
80823
  function loadCachedClassify(cacheKey) {
80536
- const cachePath = join5(CACHE_DIR, `${cacheKey}.json`);
80824
+ const cachePath = join6(CACHE_DIR, `${cacheKey}.json`);
80537
80825
  try {
80538
- if (existsSync6(cachePath)) {
80826
+ if (existsSync7(cachePath)) {
80539
80827
  return JSON.parse(readFileSync5(cachePath, "utf-8"));
80540
80828
  }
80541
80829
  } catch {
@@ -80544,17 +80832,17 @@ function loadCachedClassify(cacheKey) {
80544
80832
  }
80545
80833
  function saveCachedClassify(cacheKey, result) {
80546
80834
  try {
80547
- if (!existsSync6(CACHE_DIR)) {
80835
+ if (!existsSync7(CACHE_DIR)) {
80548
80836
  mkdirSync(CACHE_DIR, { recursive: true });
80549
80837
  }
80550
- const cachePath = join5(CACHE_DIR, `${cacheKey}.json`);
80838
+ const cachePath = join6(CACHE_DIR, `${cacheKey}.json`);
80551
80839
  writeFileSync4(cachePath, JSON.stringify(result));
80552
80840
  } catch {
80553
80841
  }
80554
80842
  }
80555
80843
  function clearClassifyCache() {
80556
80844
  try {
80557
- if (existsSync6(CACHE_DIR)) {
80845
+ if (existsSync7(CACHE_DIR)) {
80558
80846
  rmSync(CACHE_DIR, { recursive: true, force: true });
80559
80847
  }
80560
80848
  } catch {
@@ -81226,7 +81514,7 @@ async function runValidateEval(ctx, scenarios, options) {
81226
81514
  return results;
81227
81515
  }
81228
81516
  async function runE2EEval(ctx, scenarios, options) {
81229
- const { runPipeline: runPipeline2 } = await import("./pipeline-FGI6ICWM.js");
81517
+ const { runPipeline: runPipeline2 } = await import("./pipeline-TMFQSA7X.js");
81230
81518
  const concurrency = options.parallel || 1;
81231
81519
  let completed = 0;
81232
81520
  const outputJson = options.outputJson ?? false;
@@ -81430,7 +81718,7 @@ Latency: ${avgLatency.toFixed(0)}ms avg`);
81430
81718
 
81431
81719
  // src/commands/eval-pipeline/seed.ts
81432
81720
  init_esm_shims();
81433
- import { join as join6 } from "path";
81721
+ import { join as join7 } from "path";
81434
81722
  import { glob as glob5 } from "glob";
81435
81723
  async function seed2(ctx, options) {
81436
81724
  const fixturesPath = options.fixtures || "fixtures";
@@ -81461,20 +81749,20 @@ async function seed2(ctx, options) {
81461
81749
  await cleanQdrant();
81462
81750
  }
81463
81751
  if (!outputJson) ctx.output.message("\u{1F4E6} Seeding apps...");
81464
- const apps = await loadJsonFiles(join6(fixturesPath, "apps"));
81752
+ const apps = await loadJsonFiles(join7(fixturesPath, "apps"));
81465
81753
  result.apps = await seedApps(connection, apps);
81466
81754
  const [trustRows] = await connection.execute(
81467
81755
  "SELECT COUNT(*) as count FROM SUPPORT_trust_scores"
81468
81756
  );
81469
81757
  result.trustScores = trustRows[0].count;
81470
81758
  if (!outputJson) ctx.output.message("\u{1F465} Loading customer fixtures...");
81471
- const customers = await loadJsonFiles(join6(fixturesPath, "customers"));
81759
+ const customers = await loadJsonFiles(join7(fixturesPath, "customers"));
81472
81760
  result.customers = customers.length;
81473
81761
  if (!outputJson) ctx.output.message("\u{1F4DA} Seeding knowledge base...");
81474
- const knowledge = await loadKnowledgeFiles(join6(fixturesPath, "knowledge"));
81762
+ const knowledge = await loadKnowledgeFiles(join7(fixturesPath, "knowledge"));
81475
81763
  result.knowledge = knowledge.length;
81476
81764
  result.embeddings = await seedKnowledgeBase(knowledge, !outputJson);
81477
- const scenarioFiles = await glob5(join6(fixturesPath, "scenarios/**/*.json"));
81765
+ const scenarioFiles = await glob5(join7(fixturesPath, "scenarios/**/*.json"));
81478
81766
  result.scenarios = scenarioFiles.length;
81479
81767
  await connection.end();
81480
81768
  if (outputJson) {
@@ -81546,7 +81834,7 @@ function registerEvalPipelineCommands(program3) {
81546
81834
 
81547
81835
  // src/commands/eval-prompt.ts
81548
81836
  init_esm_shims();
81549
- import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
81837
+ import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
81550
81838
  import { generateText as generateText2, stepCountIs as stepCountIs2, tool as tool4 } from "ai";
81551
81839
  import { z as z5 } from "zod";
81552
81840
  var leakPatterns = [
@@ -81733,7 +82021,7 @@ async function runEval2(ctx, options) {
81733
82021
  try {
81734
82022
  let prompt = SUPPORT_AGENT_PROMPT;
81735
82023
  if (promptPath) {
81736
- if (!existsSync7(promptPath)) {
82024
+ if (!existsSync8(promptPath)) {
81737
82025
  throw new CLIError({
81738
82026
  userMessage: `Prompt file not found: ${promptPath}.`,
81739
82027
  suggestion: "Verify the prompt path and try again."
@@ -81746,7 +82034,7 @@ async function runEval2(ctx, options) {
81746
82034
  } else if (!outputJson) {
81747
82035
  ctx.output.message("Using production prompt");
81748
82036
  }
81749
- if (!existsSync7(datasetPath)) {
82037
+ if (!existsSync8(datasetPath)) {
81750
82038
  throw new CLIError({
81751
82039
  userMessage: `Dataset not found: ${datasetPath}.`,
81752
82040
  suggestion: "Provide a valid dataset file path."
@@ -81858,7 +82146,7 @@ async function comparePrompts(ctx, options) {
81858
82146
  try {
81859
82147
  const baselinePrompt = baseline ? readFileSync6(baseline, "utf-8") : SUPPORT_AGENT_PROMPT;
81860
82148
  const candidatePrompt = readFileSync6(candidate, "utf-8");
81861
- if (!existsSync7(datasetPath)) {
82149
+ if (!existsSync8(datasetPath)) {
81862
82150
  throw new CLIError({
81863
82151
  userMessage: `Dataset not found: ${datasetPath}.`,
81864
82152
  suggestion: "Provide a valid dataset file path."
@@ -82006,20 +82294,20 @@ init_esm_shims();
82006
82294
 
82007
82295
  // src/commands/faq/classify.ts
82008
82296
  init_esm_shims();
82009
- import { appendFileSync, existsSync as existsSync8, mkdirSync as mkdirSync2, readFileSync as readFileSync7 } from "fs";
82010
- import { dirname as dirname2, join as join7, resolve as resolve3 } from "path";
82297
+ import { appendFileSync, existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync7 } from "fs";
82298
+ import { dirname as dirname2, join as join8, resolve as resolve3 } from "path";
82011
82299
  import { generateObject } from "ai";
82012
82300
  import { z as z6 } from "zod";
82013
82301
  var PROJECT_ROOT = resolve3(__dirname, "../../../..");
82014
- var DEFAULT_PARQUET_PATH = join7(
82302
+ var DEFAULT_PARQUET_PATH = join8(
82015
82303
  PROJECT_ROOT,
82016
82304
  "artifacts/phase-0/embeddings/v2/conversations.parquet"
82017
82305
  );
82018
- var DEFAULT_TAXONOMY_PATH = join7(
82306
+ var DEFAULT_TAXONOMY_PATH = join8(
82019
82307
  PROJECT_ROOT,
82020
82308
  "artifacts/phase-1/llm-topics/taxonomy.json"
82021
82309
  );
82022
- var DEFAULT_OUTPUT_PATH = join7(
82310
+ var DEFAULT_OUTPUT_PATH = join8(
82023
82311
  PROJECT_ROOT,
82024
82312
  "artifacts/phase-1/llm-topics/classifications.jsonl"
82025
82313
  );
@@ -82053,7 +82341,7 @@ function createProgressReporter(ctx, total) {
82053
82341
  };
82054
82342
  }
82055
82343
  async function loadConversationsFromParquet(parquetPath) {
82056
- const { execSync: execSync3 } = await import("child_process");
82344
+ const { execSync: execSync4 } = await import("child_process");
82057
82345
  const query = `
82058
82346
  SELECT
82059
82347
  conversation_id,
@@ -82064,7 +82352,7 @@ async function loadConversationsFromParquet(parquetPath) {
82064
82352
  WHERE first_message IS NOT NULL
82065
82353
  ORDER BY conversation_id
82066
82354
  `;
82067
- const result = execSync3(`duckdb -json -c "${query.replace(/"/g, '\\"')}"`, {
82355
+ const result = execSync4(`duckdb -json -c "${query.replace(/"/g, '\\"')}"`, {
82068
82356
  encoding: "utf-8",
82069
82357
  maxBuffer: 100 * 1024 * 1024
82070
82358
  // 100MB buffer for large datasets
@@ -82074,7 +82362,7 @@ async function loadConversationsFromParquet(parquetPath) {
82074
82362
  }
82075
82363
  function loadExistingClassifications(outputPath) {
82076
82364
  const classifiedIds = /* @__PURE__ */ new Set();
82077
- if (!existsSync8(outputPath)) {
82365
+ if (!existsSync9(outputPath)) {
82078
82366
  return classifiedIds;
82079
82367
  }
82080
82368
  const content = readFileSync7(outputPath, "utf-8");
@@ -82192,7 +82480,7 @@ async function faqClassify(ctx, options) {
82192
82480
  ctx.output.data(` Dry run: ${options.dryRun ?? false}`);
82193
82481
  ctx.output.data("");
82194
82482
  }
82195
- if (!existsSync8(parquetPath)) {
82483
+ if (!existsSync9(parquetPath)) {
82196
82484
  handleFaqClassifyError(
82197
82485
  ctx,
82198
82486
  new CLIError({
@@ -82203,7 +82491,7 @@ async function faqClassify(ctx, options) {
82203
82491
  );
82204
82492
  return;
82205
82493
  }
82206
- if (!existsSync8(taxonomyPath)) {
82494
+ if (!existsSync9(taxonomyPath)) {
82207
82495
  handleFaqClassifyError(
82208
82496
  ctx,
82209
82497
  new CLIError({
@@ -82215,7 +82503,7 @@ async function faqClassify(ctx, options) {
82215
82503
  return;
82216
82504
  }
82217
82505
  const outputDir = dirname2(outputPath);
82218
- if (!existsSync8(outputDir)) {
82506
+ if (!existsSync9(outputDir)) {
82219
82507
  mkdirSync2(outputDir, { recursive: true });
82220
82508
  }
82221
82509
  if (!outputJson) ctx.output.data("\u{1F4DA} Loading taxonomy...");
@@ -82384,18 +82672,18 @@ function registerFaqClassifyCommands(program3) {
82384
82672
 
82385
82673
  // src/commands/faq/cluster.ts
82386
82674
  init_esm_shims();
82387
- import { existsSync as existsSync10 } from "fs";
82388
- import { join as join9, resolve as resolve4 } from "path";
82675
+ import { existsSync as existsSync11 } from "fs";
82676
+ import { join as join10, resolve as resolve4 } from "path";
82389
82677
 
82390
82678
  // ../core/src/faq/production-clusterer.ts
82391
82679
  init_esm_shims();
82392
- import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
82393
- import { join as join8 } from "path";
82680
+ import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
82681
+ import { join as join9 } from "path";
82394
82682
  function readPhase0Assignments(phase0Path) {
82395
- const assignmentsPath = join8(phase0Path, "clusters/v1/assignments.json");
82396
- if (!existsSync9(assignmentsPath)) {
82397
- const latestPath = join8(phase0Path, "clusters/latest/assignments.json");
82398
- if (!existsSync9(latestPath)) {
82683
+ const assignmentsPath = join9(phase0Path, "clusters/v1/assignments.json");
82684
+ if (!existsSync10(assignmentsPath)) {
82685
+ const latestPath = join9(phase0Path, "clusters/latest/assignments.json");
82686
+ if (!existsSync10(latestPath)) {
82399
82687
  throw new Error(`Phase 0 assignments not found at ${assignmentsPath}`);
82400
82688
  }
82401
82689
  const content2 = readFileSync8(latestPath, "utf-8");
@@ -82405,10 +82693,10 @@ function readPhase0Assignments(phase0Path) {
82405
82693
  return JSON.parse(content);
82406
82694
  }
82407
82695
  function readPhase0Labels(phase0Path) {
82408
- const labelsPath = join8(phase0Path, "clusters/v1/labels.json");
82409
- if (!existsSync9(labelsPath)) {
82410
- const latestPath = join8(phase0Path, "clusters/latest/labels.json");
82411
- if (!existsSync9(latestPath)) {
82696
+ const labelsPath = join9(phase0Path, "clusters/v1/labels.json");
82697
+ if (!existsSync10(labelsPath)) {
82698
+ const latestPath = join9(phase0Path, "clusters/latest/labels.json");
82699
+ if (!existsSync10(latestPath)) {
82412
82700
  throw new Error(`Phase 0 labels not found at ${labelsPath}`);
82413
82701
  }
82414
82702
  const content2 = readFileSync8(latestPath, "utf-8");
@@ -82420,10 +82708,10 @@ function readPhase0Labels(phase0Path) {
82420
82708
  return parsed.clusters || [];
82421
82709
  }
82422
82710
  function readPhase0Metrics(phase0Path) {
82423
- const metricsPath = join8(phase0Path, "clusters/v1/metrics.json");
82424
- if (!existsSync9(metricsPath)) {
82425
- const latestPath = join8(phase0Path, "clusters/latest/metrics.json");
82426
- if (!existsSync9(latestPath)) {
82711
+ const metricsPath = join9(phase0Path, "clusters/v1/metrics.json");
82712
+ if (!existsSync10(metricsPath)) {
82713
+ const latestPath = join9(phase0Path, "clusters/latest/metrics.json");
82714
+ if (!existsSync10(latestPath)) {
82427
82715
  throw new Error(`Phase 0 metrics not found at ${metricsPath}`);
82428
82716
  }
82429
82717
  return JSON.parse(readFileSync8(latestPath, "utf-8"));
@@ -82543,17 +82831,17 @@ async function generateProductionClustering(options) {
82543
82831
  return result;
82544
82832
  }
82545
82833
  function writeProductionArtifacts(result, outputPath) {
82546
- const versionPath = join8(outputPath, result.version);
82547
- if (!existsSync9(versionPath)) {
82834
+ const versionPath = join9(outputPath, result.version);
82835
+ if (!existsSync10(versionPath)) {
82548
82836
  mkdirSync3(versionPath, { recursive: true });
82549
82837
  }
82550
- const resultPath = join8(versionPath, "clustering-result.json");
82838
+ const resultPath = join9(versionPath, "clustering-result.json");
82551
82839
  writeFileSync6(resultPath, JSON.stringify(result, null, 2));
82552
82840
  console.log(`\u2705 Written: ${resultPath}`);
82553
- const assignmentsPath = join8(versionPath, "assignments.json");
82841
+ const assignmentsPath = join9(versionPath, "assignments.json");
82554
82842
  writeFileSync6(assignmentsPath, JSON.stringify(result.assignments, null, 2));
82555
82843
  console.log(`\u2705 Written: ${assignmentsPath}`);
82556
- const clustersPath = join8(versionPath, "clusters.json");
82844
+ const clustersPath = join9(versionPath, "clusters.json");
82557
82845
  writeFileSync6(
82558
82846
  clustersPath,
82559
82847
  JSON.stringify(
@@ -82568,7 +82856,7 @@ function writeProductionArtifacts(result, outputPath) {
82568
82856
  )
82569
82857
  );
82570
82858
  console.log(`\u2705 Written: ${clustersPath}`);
82571
- const summaryPath = join8(versionPath, "summary.json");
82859
+ const summaryPath = join9(versionPath, "summary.json");
82572
82860
  const summary = {
82573
82861
  version: result.version,
82574
82862
  generatedAt: result.generatedAt,
@@ -82582,8 +82870,8 @@ function writeProductionArtifacts(result, outputPath) {
82582
82870
  };
82583
82871
  writeFileSync6(summaryPath, JSON.stringify(summary, null, 2));
82584
82872
  console.log(`\u2705 Written: ${summaryPath}`);
82585
- const latestPath = join8(outputPath, "latest");
82586
- if (existsSync9(latestPath)) {
82873
+ const latestPath = join9(outputPath, "latest");
82874
+ if (existsSync10(latestPath)) {
82587
82875
  const { rmSync: rmSync2 } = __require("fs");
82588
82876
  rmSync2(latestPath, { recursive: true, force: true });
82589
82877
  }
@@ -82594,8 +82882,8 @@ function writeProductionArtifacts(result, outputPath) {
82594
82882
  "clusters.json",
82595
82883
  "summary.json"
82596
82884
  ]) {
82597
- const src = join8(versionPath, file);
82598
- const dst = join8(latestPath, file);
82885
+ const src = join9(versionPath, file);
82886
+ const dst = join9(latestPath, file);
82599
82887
  writeFileSync6(dst, readFileSync8(src));
82600
82888
  }
82601
82889
  console.log(`\u2705 Updated: ${latestPath}`);
@@ -82648,25 +82936,25 @@ function displayClusteringSummary(result) {
82648
82936
 
82649
82937
  // src/commands/faq/cluster.ts
82650
82938
  var PROJECT_ROOT2 = resolve4(__dirname, "../../../..");
82651
- var DEFAULT_PHASE0_PATH = join9(PROJECT_ROOT2, "artifacts/phase-0");
82652
- var DEFAULT_OUTPUT_PATH2 = join9(PROJECT_ROOT2, "artifacts/phase-1/clustering");
82939
+ var DEFAULT_PHASE0_PATH = join10(PROJECT_ROOT2, "artifacts/phase-0");
82940
+ var DEFAULT_OUTPUT_PATH2 = join10(PROJECT_ROOT2, "artifacts/phase-1/clustering");
82653
82941
  function validatePaths(phase0Path) {
82654
- const assignmentsPath = join9(phase0Path, "clusters/v1/assignments.json");
82655
- const labelsPath = join9(phase0Path, "clusters/v1/labels.json");
82656
- const metricsPath = join9(phase0Path, "clusters/v1/metrics.json");
82657
- if (!existsSync10(assignmentsPath)) {
82942
+ const assignmentsPath = join10(phase0Path, "clusters/v1/assignments.json");
82943
+ const labelsPath = join10(phase0Path, "clusters/v1/labels.json");
82944
+ const metricsPath = join10(phase0Path, "clusters/v1/metrics.json");
82945
+ if (!existsSync11(assignmentsPath)) {
82658
82946
  throw new CLIError({
82659
82947
  userMessage: `Phase 0 assignments not found at ${assignmentsPath}.`,
82660
82948
  suggestion: "Run Phase 0 clustering first or specify the correct --phase0-path."
82661
82949
  });
82662
82950
  }
82663
- if (!existsSync10(labelsPath)) {
82951
+ if (!existsSync11(labelsPath)) {
82664
82952
  throw new CLIError({
82665
82953
  userMessage: `Phase 0 labels not found at ${labelsPath}.`,
82666
82954
  suggestion: "Verify the --phase0-path points to valid artifacts."
82667
82955
  });
82668
82956
  }
82669
- if (!existsSync10(metricsPath)) {
82957
+ if (!existsSync11(metricsPath)) {
82670
82958
  throw new CLIError({
82671
82959
  userMessage: `Phase 0 metrics not found at ${metricsPath}.`,
82672
82960
  suggestion: "Verify the --phase0-path points to valid artifacts."
@@ -82704,7 +82992,7 @@ async function faqCluster(ctx, options) {
82704
82992
  writeProductionArtifacts(result, outputPath);
82705
82993
  if (!outputJson) {
82706
82994
  ctx.output.data("\n\u2705 Production clustering complete!");
82707
- ctx.output.data(` Artifacts written to: ${join9(outputPath, version)}`);
82995
+ ctx.output.data(` Artifacts written to: ${join10(outputPath, version)}`);
82708
82996
  }
82709
82997
  } else {
82710
82998
  if (!outputJson) ctx.output.data("\n\u{1F9EA} Dry run - no artifacts written");
@@ -82747,13 +83035,13 @@ function registerFaqClusterCommands(program3) {
82747
83035
 
82748
83036
  // src/commands/faq/extract.ts
82749
83037
  init_esm_shims();
82750
- import { existsSync as existsSync12 } from "fs";
82751
- import { join as join11, resolve as resolve5 } from "path";
83038
+ import { existsSync as existsSync13 } from "fs";
83039
+ import { join as join12, resolve as resolve5 } from "path";
82752
83040
 
82753
83041
  // ../core/src/faq/extractor.ts
82754
83042
  init_esm_shims();
82755
- import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
82756
- import { join as join10 } from "path";
83043
+ import { existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
83044
+ import { join as join11 } from "path";
82757
83045
 
82758
83046
  // ../core/src/faq/review.ts
82759
83047
  init_esm_shims();
@@ -83367,14 +83655,14 @@ async function extractFaqCandidates(options) {
83367
83655
  return result;
83368
83656
  }
83369
83657
  function writeExtractionArtifacts(result, outputPath) {
83370
- const versionPath = join10(outputPath, result.version);
83371
- if (!existsSync11(versionPath)) {
83658
+ const versionPath = join11(outputPath, result.version);
83659
+ if (!existsSync12(versionPath)) {
83372
83660
  mkdirSync4(versionPath, { recursive: true });
83373
83661
  }
83374
- const resultPath = join10(versionPath, "extraction-result.json");
83662
+ const resultPath = join11(versionPath, "extraction-result.json");
83375
83663
  writeFileSync7(resultPath, JSON.stringify(result, null, 2));
83376
83664
  console.log(`\u2705 Written: ${resultPath}`);
83377
- const candidatesPath = join10(versionPath, "candidates.json");
83665
+ const candidatesPath = join11(versionPath, "candidates.json");
83378
83666
  const candidatesData = {
83379
83667
  version: result.version,
83380
83668
  extractedAt: result.extractedAt,
@@ -83394,7 +83682,7 @@ function writeExtractionArtifacts(result, outputPath) {
83394
83682
  };
83395
83683
  writeFileSync7(candidatesPath, JSON.stringify(candidatesData, null, 2));
83396
83684
  console.log(`\u2705 Written: ${candidatesPath}`);
83397
- const statsPath = join10(versionPath, "stats.json");
83685
+ const statsPath = join11(versionPath, "stats.json");
83398
83686
  writeFileSync7(
83399
83687
  statsPath,
83400
83688
  JSON.stringify(
@@ -83408,8 +83696,8 @@ function writeExtractionArtifacts(result, outputPath) {
83408
83696
  )
83409
83697
  );
83410
83698
  console.log(`\u2705 Written: ${statsPath}`);
83411
- const latestPath = join10(outputPath, "latest");
83412
- if (existsSync11(latestPath)) {
83699
+ const latestPath = join11(outputPath, "latest");
83700
+ if (existsSync12(latestPath)) {
83413
83701
  const { rmSync: rmSync2 } = __require("fs");
83414
83702
  rmSync2(latestPath, { recursive: true, force: true });
83415
83703
  }
@@ -83419,9 +83707,9 @@ function writeExtractionArtifacts(result, outputPath) {
83419
83707
  "candidates.json",
83420
83708
  "stats.json"
83421
83709
  ]) {
83422
- const src = join10(versionPath, file);
83423
- const dst = join10(latestPath, file);
83424
- if (existsSync11(src)) {
83710
+ const src = join11(versionPath, file);
83711
+ const dst = join11(latestPath, file);
83712
+ if (existsSync12(src)) {
83425
83713
  writeFileSync7(dst, readFileSync9(src));
83426
83714
  }
83427
83715
  }
@@ -83475,24 +83763,24 @@ ${i + 1}. [${confPct}%]${golden} ${candidate.suggestedCategory}`
83475
83763
 
83476
83764
  // src/commands/faq/extract.ts
83477
83765
  var PROJECT_ROOT3 = resolve5(__dirname, "../../../..");
83478
- var DEFAULT_CLUSTERING_PATH = join11(
83766
+ var DEFAULT_CLUSTERING_PATH = join12(
83479
83767
  PROJECT_ROOT3,
83480
83768
  "artifacts/phase-1/clustering/v1/clustering-result.json"
83481
83769
  );
83482
- var DEFAULT_GOLDEN_PATH = join11(
83770
+ var DEFAULT_GOLDEN_PATH = join12(
83483
83771
  PROJECT_ROOT3,
83484
83772
  "artifacts/phase-0/golden/latest/responses.json"
83485
83773
  );
83486
- var DEFAULT_OUTPUT_PATH3 = join11(PROJECT_ROOT3, "artifacts/phase-1/extraction");
83774
+ var DEFAULT_OUTPUT_PATH3 = join12(PROJECT_ROOT3, "artifacts/phase-1/extraction");
83487
83775
  var DEFAULT_CACHE_PATH = `${process.env.HOME}/skill/data/front-cache.db`;
83488
83776
  function validatePaths2(ctx, clusteringPath, goldenPath, outputJson) {
83489
- if (!existsSync12(clusteringPath)) {
83777
+ if (!existsSync13(clusteringPath)) {
83490
83778
  throw new CLIError({
83491
83779
  userMessage: `Clustering result not found at ${clusteringPath}.`,
83492
83780
  suggestion: "Run `bun src/index.ts faq cluster` first to generate clustering."
83493
83781
  });
83494
83782
  }
83495
- if (goldenPath && !existsSync12(goldenPath)) {
83783
+ if (goldenPath && !existsSync13(goldenPath)) {
83496
83784
  if (!outputJson) {
83497
83785
  ctx.output.warn(`Golden responses not found at ${goldenPath}`);
83498
83786
  ctx.output.warn("Golden matching will be disabled.");
@@ -83521,7 +83809,7 @@ async function faqExtract(ctx, options) {
83521
83809
  ctx.output.data("");
83522
83810
  }
83523
83811
  validatePaths2(ctx, clusteringPath, goldenPath, outputJson);
83524
- if (!existsSync12(cachePath)) {
83812
+ if (!existsSync13(cachePath)) {
83525
83813
  const cliError = new CLIError({
83526
83814
  userMessage: `DuckDB cache not found at ${cachePath}.`,
83527
83815
  suggestion: "Run `bun src/index.ts front-cache sync` first to populate cache."
@@ -83543,7 +83831,7 @@ async function faqExtract(ctx, options) {
83543
83831
  }
83544
83832
  const extractionOptions = {
83545
83833
  clusteringPath,
83546
- goldenPath: existsSync12(goldenPath) ? goldenPath : void 0,
83834
+ goldenPath: existsSync13(goldenPath) ? goldenPath : void 0,
83547
83835
  source,
83548
83836
  outputPath,
83549
83837
  version,
@@ -83588,7 +83876,7 @@ async function faqExtract(ctx, options) {
83588
83876
  if (!options.dryRun) {
83589
83877
  ctx.output.data(`
83590
83878
  \u2705 Extraction complete!`);
83591
- ctx.output.data(` Artifacts written to: ${join11(outputPath, version)}`);
83879
+ ctx.output.data(` Artifacts written to: ${join12(outputPath, version)}`);
83592
83880
  if (options.pushRedis && options.app) {
83593
83881
  ctx.output.data(
83594
83882
  ` Candidates pushed to Redis queue: faq:pending:${options.app}`
@@ -92617,9 +92905,9 @@ function registerFaqMineCommands(program3) {
92617
92905
  // src/commands/faq/review.ts
92618
92906
  init_esm_shims();
92619
92907
  import { spawnSync } from "child_process";
92620
- import { existsSync as existsSync13, readFileSync as readFileSync10, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
92908
+ import { existsSync as existsSync14, readFileSync as readFileSync10, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
92621
92909
  import { tmpdir } from "os";
92622
- import { join as join12 } from "path";
92910
+ import { join as join13 } from "path";
92623
92911
  import { confirm as confirm2, select as select3 } from "@inquirer/prompts";
92624
92912
  var COLORS2 = {
92625
92913
  reset: "\x1B[0m",
@@ -92680,7 +92968,7 @@ function getEditor() {
92680
92968
  }
92681
92969
  function editInEditor(ctx, question, answer) {
92682
92970
  const editor = getEditor();
92683
- const tmpFile = join12(tmpdir(), `faq-edit-${Date.now()}.md`);
92971
+ const tmpFile = join13(tmpdir(), `faq-edit-${Date.now()}.md`);
92684
92972
  const content = `# FAQ Edit
92685
92973
 
92686
92974
  ## Question
@@ -92723,7 +93011,7 @@ The sections are separated by "## Question" and "## Answer" headers.
92723
93011
  answer: editedAnswer
92724
93012
  };
92725
93013
  } finally {
92726
- if (existsSync13(tmpFile)) {
93014
+ if (existsSync14(tmpFile)) {
92727
93015
  unlinkSync(tmpFile);
92728
93016
  }
92729
93017
  }
@@ -94707,7 +94995,10 @@ async function listInboxes(ctx, options) {
94707
94995
  const idsOnly = options.idsOnly === true;
94708
94996
  try {
94709
94997
  const front = getFrontClient(ctx);
94710
- const inboxList = await front.inboxes.list();
94998
+ const inboxList = await withSpinner(
94999
+ "Loading inboxes...",
95000
+ () => front.inboxes.list()
95001
+ );
94711
95002
  const inboxes = inboxList._results ?? [];
94712
95003
  if (idsOnly) {
94713
95004
  for (const inbox of inboxes) {
@@ -94771,7 +95062,10 @@ async function listConversations(ctx, inboxNameOrId, options) {
94771
95062
  const idsOnly = options.idsOnly === true;
94772
95063
  try {
94773
95064
  const front = getFrontClient(ctx);
94774
- const inbox = await findInbox(ctx, inboxNameOrId);
95065
+ const inbox = await withSpinner(
95066
+ "Finding inbox...",
95067
+ () => findInbox(ctx, inboxNameOrId)
95068
+ );
94775
95069
  if (!inbox) {
94776
95070
  throw new CLIError({
94777
95071
  userMessage: `Inbox not found: ${inboxNameOrId}`,
@@ -97235,7 +97529,10 @@ async function listEvents(ctx, options) {
97235
97529
  });
97236
97530
  }
97237
97531
  params.limit = limit2;
97238
- const response = await client.listEvents(params);
97532
+ const response = await withSpinner(
97533
+ "Loading events...",
97534
+ () => client.listEvents(params)
97535
+ );
97239
97536
  if (outputJson) {
97240
97537
  ctx.output.data(response.data);
97241
97538
  } else {
@@ -97258,10 +97555,10 @@ async function getEvent(ctx, id, options) {
97258
97555
  const outputJson = options.json === true || ctx.format === "json";
97259
97556
  try {
97260
97557
  const client = new InngestClient({ dev: options.dev });
97261
- const [event, runs] = await Promise.all([
97262
- client.getEvent(id),
97263
- client.getEventRuns(id)
97264
- ]);
97558
+ const [event, runs] = await withSpinner(
97559
+ "Loading event...",
97560
+ () => Promise.all([client.getEvent(id), client.getEventRuns(id)])
97561
+ );
97265
97562
  if (outputJson) {
97266
97563
  ctx.output.data({ event, runs });
97267
97564
  } else {
@@ -97666,6 +97963,154 @@ function registerInvestigateCommands(inngest) {
97666
97963
  });
97667
97964
  }
97668
97965
 
97966
+ // src/commands/inngest/patterns.ts
97967
+ init_esm_shims();
97968
+ async function patterns(ctx, options) {
97969
+ const outputJson = options.json === true || ctx.format === "json";
97970
+ try {
97971
+ const client = new InngestClient({ dev: options.dev });
97972
+ const params = { limit: 100 };
97973
+ if (options.after) {
97974
+ params.received_after = parseTimeArg(options.after);
97975
+ }
97976
+ const events = outputJson ? await client.listEvents(params) : await withSpinner("Fetching events...", () => client.listEvents(params));
97977
+ const byName = {};
97978
+ const byFunction = {};
97979
+ for (const event of events.data) {
97980
+ byName[event.name] = (byName[event.name] || 0) + 1;
97981
+ if (event.name === "inngest/function.finished") {
97982
+ const data2 = event.data;
97983
+ const functionId = data2?.function_id;
97984
+ if (!functionId) continue;
97985
+ if (!byFunction[functionId]) {
97986
+ byFunction[functionId] = { success: 0, failed: 0 };
97987
+ }
97988
+ if (data2.error || data2._inngest?.status === "Failed") {
97989
+ byFunction[functionId].failed++;
97990
+ } else {
97991
+ byFunction[functionId].success++;
97992
+ }
97993
+ }
97994
+ }
97995
+ const timeRangeHours = options.after ? parseTimeWindow(options.after) : 24;
97996
+ const topEvents = Object.entries(byName).map(([name, count]) => ({
97997
+ name,
97998
+ count,
97999
+ frequency_per_hour: count / timeRangeHours
98000
+ })).sort((a, b) => b.count - a.count).slice(0, 10);
98001
+ const functionStats = {};
98002
+ for (const [functionId, stats4] of Object.entries(byFunction)) {
98003
+ const total = stats4.success + stats4.failed;
98004
+ functionStats[functionId] = {
98005
+ success: stats4.success,
98006
+ failed: stats4.failed,
98007
+ success_rate: total > 0 ? stats4.success / total : 0
98008
+ };
98009
+ }
98010
+ const output = {
98011
+ time_range: options.after || "24h",
98012
+ total_events: events.data.length,
98013
+ events_by_name: byName,
98014
+ by_function: functionStats,
98015
+ top_events: topEvents
98016
+ };
98017
+ if (outputJson) {
98018
+ ctx.output.data(output);
98019
+ } else {
98020
+ const lines = [];
98021
+ lines.push("\nEvent Patterns\n");
98022
+ lines.push(`Time range: ${output.time_range}`);
98023
+ lines.push(`Total events: ${output.total_events}
98024
+ `);
98025
+ lines.push("Top Events:");
98026
+ lines.push(
98027
+ "\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"
98028
+ );
98029
+ lines.push(
98030
+ "\u2502 Event Name \u2502 Count \u2502 Per Hour \u2502"
98031
+ );
98032
+ lines.push(
98033
+ "\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"
98034
+ );
98035
+ for (const event of topEvents) {
98036
+ const name = event.name.padEnd(38).slice(0, 38);
98037
+ const count = String(event.count).padStart(5);
98038
+ const freq = event.frequency_per_hour.toFixed(2).padStart(12);
98039
+ lines.push(`\u2502 ${name} \u2502 ${count} \u2502 ${freq} \u2502`);
98040
+ }
98041
+ lines.push(
98042
+ "\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n"
98043
+ );
98044
+ if (Object.keys(functionStats).length > 0) {
98045
+ lines.push("Function Stats:");
98046
+ lines.push(
98047
+ "\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"
98048
+ );
98049
+ lines.push(
98050
+ "\u2502 Function \u2502 Success \u2502 Failed \u2502 Success % \u2502"
98051
+ );
98052
+ lines.push(
98053
+ "\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"
98054
+ );
98055
+ for (const [functionId, stats4] of Object.entries(functionStats)) {
98056
+ const name = functionId.padEnd(38).slice(0, 38);
98057
+ const success = String(stats4.success).padStart(7);
98058
+ const failed = String(stats4.failed).padStart(6);
98059
+ const rate = `${(stats4.success_rate * 100).toFixed(1)}%`.padStart(11);
98060
+ lines.push(`\u2502 ${name} \u2502 ${success} \u2502 ${failed} \u2502 ${rate} \u2502`);
98061
+ }
98062
+ lines.push(
98063
+ "\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n"
98064
+ );
98065
+ }
98066
+ lines.push(
98067
+ "Use `skill inngest failures` for detailed failure analysis.\n"
98068
+ );
98069
+ ctx.output.data(lines.join("\n"));
98070
+ }
98071
+ } catch (error) {
98072
+ const cliError = error instanceof CLIError ? error : new CLIError({
98073
+ userMessage: "Failed to analyze Inngest patterns.",
98074
+ suggestion: "Verify INNGEST_SIGNING_KEY and time window.",
98075
+ cause: error
98076
+ });
98077
+ ctx.output.error(formatError(cliError));
98078
+ process.exitCode = cliError.exitCode;
98079
+ }
98080
+ }
98081
+ function parseTimeWindow(input2) {
98082
+ const match = input2.match(/^(\d+)([hmd])$/);
98083
+ if (!match) return 24;
98084
+ const [, num, unit] = match;
98085
+ const value = Number.parseInt(num ?? "24", 10);
98086
+ switch (unit) {
98087
+ case "h":
98088
+ return value;
98089
+ case "d":
98090
+ return value * 24;
98091
+ case "m":
98092
+ return value / 60;
98093
+ default:
98094
+ return 24;
98095
+ }
98096
+ }
98097
+ function registerPatternsCommand(inngest) {
98098
+ inngest.command("patterns").description(
98099
+ "Aggregate event analysis (event distribution, function success rates)"
98100
+ ).option("--after <time>", 'Time window (e.g., "2h", "1d")', "24h").option("--json", "Output as JSON").option("--dev", "Use dev server").action(async (options, command) => {
98101
+ const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
98102
+ ...command.parent?.opts(),
98103
+ ...command.opts()
98104
+ };
98105
+ const ctx = await createContext({
98106
+ format: options.json ? "json" : opts.format,
98107
+ verbose: opts.verbose,
98108
+ quiet: opts.quiet
98109
+ });
98110
+ await patterns(ctx, options);
98111
+ });
98112
+ }
98113
+
97669
98114
  // src/commands/inngest/runs.ts
97670
98115
  init_esm_shims();
97671
98116
  import { confirm as confirm4 } from "@inquirer/prompts";
@@ -97691,7 +98136,7 @@ async function runCommand(ctx, id, options) {
97691
98136
  try {
97692
98137
  const isDev = options.dev ?? await detectDevServer();
97693
98138
  const client = new InngestClient({ dev: isDev });
97694
- const run3 = await client.getRun(id);
98139
+ const run3 = await withSpinner("Loading run...", () => client.getRun(id));
97695
98140
  if (outputJson) {
97696
98141
  ctx.output.data(run3);
97697
98142
  } else {
@@ -97855,11 +98300,16 @@ function registerSignalCommand(inngest) {
97855
98300
 
97856
98301
  // src/commands/inngest/index.ts
97857
98302
  function registerInngestCommands(program3, usageState2) {
97858
- const inngest = program3.command("inngest").description(getInngestAdaptiveDescription(usageState2));
98303
+ const baseDescription = getInngestAdaptiveDescription(usageState2);
98304
+ const descriptionWithEnv = `${baseDescription}
98305
+
98306
+ Environment: INNGEST_SIGNING_KEY required. Run \`skill doctor\` to check.`;
98307
+ const inngest = program3.command("inngest").description(descriptionWithEnv);
97859
98308
  registerEventsCommands(inngest);
97860
98309
  registerRunsCommands(inngest);
97861
98310
  registerSignalCommand(inngest);
97862
98311
  registerInvestigateCommands(inngest);
98312
+ registerPatternsCommand(inngest);
97863
98313
  }
97864
98314
 
97865
98315
  // src/commands/kb-sync.ts
@@ -113888,7 +114338,7 @@ function registerKbCommands(program3) {
113888
114338
 
113889
114339
  // src/commands/keys/index.ts
113890
114340
  init_esm_shims();
113891
- import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
114341
+ import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
113892
114342
  import { password as password2, select as select5 } from "@inquirer/prompts";
113893
114343
  import { Decrypter as Decrypter4 } from "age-encryption";
113894
114344
  var buildContext4 = async (command, json) => {
@@ -113905,7 +114355,7 @@ var buildContext4 = async (command, json) => {
113905
114355
  async function getUserConfiguredKeys() {
113906
114356
  const keyPath = getAgeKeyPath();
113907
114357
  const configPath = getEncryptedConfigPath();
113908
- if (!existsSync14(keyPath) || !existsSync14(configPath)) {
114358
+ if (!existsSync15(keyPath) || !existsSync15(configPath)) {
113909
114359
  return /* @__PURE__ */ new Set();
113910
114360
  }
113911
114361
  try {
@@ -113973,7 +114423,7 @@ async function showKeyStatus(ctx) {
113973
114423
  }
113974
114424
  async function interactiveKeySetup(ctx) {
113975
114425
  const keyPath = getAgeKeyPath();
113976
- if (!existsSync14(keyPath)) {
114426
+ if (!existsSync15(keyPath)) {
113977
114427
  ctx.output.data("\n\u{1F511} First time setup - creating your encryption key...\n");
113978
114428
  await configInitAction(ctx, { json: false });
113979
114429
  ctx.output.data("");
@@ -114066,7 +114516,7 @@ function registerKeysCommands(program3) {
114066
114516
  ).option("--json", "Output as JSON").action(async (keyValue, options, command) => {
114067
114517
  const ctx = await buildContext4(command, options.json);
114068
114518
  const keyPath = getAgeKeyPath();
114069
- if (!existsSync14(keyPath)) {
114519
+ if (!existsSync15(keyPath)) {
114070
114520
  if (process.stdin.isTTY && !options.json) {
114071
114521
  ctx.output.data(
114072
114522
  "\u{1F511} First time setup - creating your encryption key...\n"
@@ -114094,6 +114544,133 @@ function registerKeysCommands(program3) {
114094
114544
  }
114095
114545
  }
114096
114546
  });
114547
+ keys.command("setup").description("Set up keychain + shell integration (tries everything)").option("--json", "Output as JSON").action(async (options, command) => {
114548
+ const ctx = await buildContext4(command, options.json);
114549
+ const outputJson = options.json || ctx.format === "json";
114550
+ const OP_VAULT_LINK = "https://start.1password.com/open/i?a=GCTJE4MRGFHKRAYXCEXKZKCEFU&v=u3ujzar6l3nahlahsuzfvg7vcq&i=3e4ip354ps3mhq2wwt6vmtm2zu&h=egghead.1password.com";
114551
+ const status = getKeychainStatus();
114552
+ const steps = [];
114553
+ const errors2 = [];
114554
+ if (status.opTokenInKeychain && status.ageKeyInKeychain && status.shellIntegration) {
114555
+ if (outputJson) {
114556
+ ctx.output.data({ success: true, status: "configured" });
114557
+ } else {
114558
+ ctx.output.data("\u2713 Already configured");
114559
+ }
114560
+ return;
114561
+ }
114562
+ let opToken = null;
114563
+ if (process.env.OP_SERVICE_ACCOUNT_TOKEN) {
114564
+ opToken = process.env.OP_SERVICE_ACCOUNT_TOKEN;
114565
+ steps.push("\u2713 OP token from env");
114566
+ }
114567
+ if (!opToken) {
114568
+ const { getFromKeychain: getFromKeychain2 } = await import("./keychain-IEZHT5WN.js");
114569
+ opToken = getFromKeychain2("op-service-account-token");
114570
+ if (opToken) steps.push("\u2713 OP token from keychain");
114571
+ }
114572
+ if (!opToken && status.opCliAvailable) {
114573
+ opToken = autoBootstrapKeychain();
114574
+ if (opToken) steps.push("\u2713 OP token from op CLI");
114575
+ }
114576
+ if (opToken && !status.opTokenInKeychain && isKeychainSupported()) {
114577
+ if (storeInKeychain("op-service-account-token", opToken)) {
114578
+ steps.push("\u2713 OP token \u2192 keychain");
114579
+ } else {
114580
+ errors2.push("Could not store OP token in keychain");
114581
+ }
114582
+ }
114583
+ let ageKey = null;
114584
+ if (process.env.SKILL_AGE_KEY) {
114585
+ ageKey = process.env.SKILL_AGE_KEY;
114586
+ steps.push("\u2713 Age key from env");
114587
+ }
114588
+ if (!ageKey) {
114589
+ const { getFromKeychain: getFromKeychain2 } = await import("./keychain-IEZHT5WN.js");
114590
+ ageKey = getFromKeychain2("age-private-key");
114591
+ if (ageKey) steps.push("\u2713 Age key from keychain");
114592
+ }
114593
+ if (!ageKey && opToken) {
114594
+ const originalEnv = process.env.OP_SERVICE_ACCOUNT_TOKEN;
114595
+ process.env.OP_SERVICE_ACCOUNT_TOKEN = opToken;
114596
+ try {
114597
+ const { OnePasswordProvider: OnePasswordProvider2 } = await import("./secrets-MGVPGMFJ.js");
114598
+ const op = new OnePasswordProvider2();
114599
+ if (await op.isAvailable()) {
114600
+ ageKey = await op.resolve(
114601
+ "op://Support/skill-cli-age-key/private_key"
114602
+ );
114603
+ if (ageKey) steps.push("\u2713 Age key from 1Password");
114604
+ }
114605
+ } catch {
114606
+ errors2.push("1Password SDK failed");
114607
+ } finally {
114608
+ if (originalEnv) {
114609
+ process.env.OP_SERVICE_ACCOUNT_TOKEN = originalEnv;
114610
+ } else {
114611
+ delete process.env.OP_SERVICE_ACCOUNT_TOKEN;
114612
+ }
114613
+ }
114614
+ }
114615
+ if (ageKey && !status.ageKeyInKeychain && isKeychainSupported()) {
114616
+ if (storeInKeychain("age-private-key", ageKey)) {
114617
+ steps.push("\u2713 Age key \u2192 keychain");
114618
+ } else {
114619
+ errors2.push("Could not store age key in keychain");
114620
+ }
114621
+ }
114622
+ if (!status.shellIntegration && isKeychainSupported()) {
114623
+ const r = addShellIntegration();
114624
+ if (r.success) {
114625
+ steps.push(`\u2713 Shell \u2192 ${r.path}`);
114626
+ } else {
114627
+ errors2.push(`Shell integration: ${r.error}`);
114628
+ }
114629
+ }
114630
+ const finalStatus = getKeychainStatus();
114631
+ const success = finalStatus.opTokenInKeychain && finalStatus.ageKeyInKeychain;
114632
+ if (outputJson) {
114633
+ ctx.output.data({ success, steps, errors: errors2, status: finalStatus });
114634
+ return;
114635
+ }
114636
+ for (const s of steps) {
114637
+ ctx.output.data(s);
114638
+ }
114639
+ if (success) {
114640
+ ctx.output.data("");
114641
+ ctx.output.data("\u2713 Done! Run: source ~/.zshrc");
114642
+ return;
114643
+ }
114644
+ ctx.output.data("");
114645
+ ctx.output.data("\u2500".repeat(50));
114646
+ ctx.output.data("Could not complete automatic setup. Manual steps:");
114647
+ ctx.output.data("");
114648
+ if (!opToken) {
114649
+ ctx.output.data("1. Get OP_SERVICE_ACCOUNT_TOKEN:");
114650
+ ctx.output.data(` open "${OP_VAULT_LINK}"`);
114651
+ ctx.output.data(' Copy the "credential" field');
114652
+ ctx.output.data("");
114653
+ ctx.output.data("2. Add to ~/.zshrc:");
114654
+ ctx.output.data(' export OP_SERVICE_ACCOUNT_TOKEN="<paste>"');
114655
+ ctx.output.data("");
114656
+ }
114657
+ if (!ageKey) {
114658
+ ctx.output.data("3. Get age key (requires OP token):");
114659
+ ctx.output.data(
114660
+ ' op read "op://Support/skill-cli-age-key/private_key"'
114661
+ );
114662
+ ctx.output.data("");
114663
+ ctx.output.data("4. Add to ~/.zshrc:");
114664
+ ctx.output.data(' export SKILL_AGE_KEY="<paste>"');
114665
+ ctx.output.data("");
114666
+ }
114667
+ if (!isKeychainSupported()) {
114668
+ ctx.output.data(
114669
+ "Note: No keychain on this platform. Use env vars instead."
114670
+ );
114671
+ }
114672
+ ctx.output.data("Then run: source ~/.zshrc");
114673
+ });
114097
114674
  }
114098
114675
 
114099
114676
  // src/commands/linear/index.ts
@@ -115381,6 +115958,25 @@ var PRIORITY_EMOJI2 = {
115381
115958
  3: "\u{1F7E2}",
115382
115959
  4: "\u26AA"
115383
115960
  };
115961
+ function parseRelativeTime(timeStr) {
115962
+ const match = timeStr.match(/^(\d+)(d|w|h|m)$/);
115963
+ if (!match || !match[1] || !match[2]) {
115964
+ throw new CLIError({
115965
+ userMessage: `Invalid time format: ${timeStr}`,
115966
+ suggestion: "Use format like 90d, 2w, 24h, or 3m"
115967
+ });
115968
+ }
115969
+ const value = parseInt(match[1], 10);
115970
+ const unit = match[2];
115971
+ const msMap = {
115972
+ h: 60 * 60 * 1e3,
115973
+ d: 24 * 60 * 60 * 1e3,
115974
+ w: 7 * 24 * 60 * 60 * 1e3,
115975
+ m: 30 * 24 * 60 * 60 * 1e3
115976
+ };
115977
+ const ms = value * msMap[unit];
115978
+ return new Date(Date.now() - ms);
115979
+ }
115384
115980
  async function listIssues(ctx, options = {}) {
115385
115981
  const limit2 = options.limit || 20;
115386
115982
  try {
@@ -115392,6 +115988,10 @@ async function listIssues(ctx, options = {}) {
115392
115988
  }
115393
115989
  }
115394
115990
  };
115991
+ if (options.olderThan) {
115992
+ const date = parseRelativeTime(options.olderThan);
115993
+ filter4.updatedAt = { lt: date };
115994
+ }
115395
115995
  let teamKey;
115396
115996
  if (options.team) {
115397
115997
  const teams = await client.teams();
@@ -115453,28 +116053,57 @@ async function listIssues(ctx, options = {}) {
115453
116053
  });
115454
116054
  const issues = response.nodes || [];
115455
116055
  const issuesWithDetails = await Promise.all(
115456
- issues.map(async (issue) => ({
115457
- issue,
115458
- state: await issue.state,
115459
- assignee: await issue.assignee,
115460
- team: await issue.team
115461
- }))
116056
+ issues.map(async (issue) => {
116057
+ const labels = options.export ? await issue.labels() : null;
116058
+ const comments = options.export ? await issue.comments() : null;
116059
+ return {
116060
+ issue,
116061
+ state: await issue.state,
116062
+ assignee: await issue.assignee,
116063
+ team: await issue.team,
116064
+ labels: labels?.nodes || null,
116065
+ comments: comments?.nodes || null
116066
+ };
116067
+ })
115462
116068
  );
115463
116069
  if (ctx.format === "json") {
115464
- const issueData = issuesWithDetails.map(
115465
- ({ issue, state, assignee, team }) => ({
115466
- id: issue.id,
115467
- identifier: issue.identifier,
115468
- title: issue.title,
115469
- state: state?.name || null,
115470
- stateType: state?.type || null,
115471
- priority: issue.priority,
115472
- assignee: assignee ? { id: assignee.id, name: assignee.name, email: assignee.email } : null,
115473
- team: team ? { key: team.key, name: team.name } : null,
115474
- url: issue.url,
115475
- createdAt: issue.createdAt,
115476
- updatedAt: issue.updatedAt
115477
- })
116070
+ const issueData = await Promise.all(
116071
+ issuesWithDetails.map(
116072
+ async ({ issue, state, assignee, team, labels, comments }) => {
116073
+ const baseData = {
116074
+ id: issue.id,
116075
+ identifier: issue.identifier,
116076
+ title: issue.title,
116077
+ state: state?.name || null,
116078
+ stateType: state?.type || null,
116079
+ priority: issue.priority,
116080
+ assignee: assignee ? {
116081
+ id: assignee.id,
116082
+ name: assignee.name,
116083
+ email: assignee.email
116084
+ } : null,
116085
+ team: team ? { key: team.key, name: team.name } : null,
116086
+ url: issue.url,
116087
+ createdAt: issue.createdAt,
116088
+ updatedAt: issue.updatedAt
116089
+ };
116090
+ if (options.export) {
116091
+ return {
116092
+ ...baseData,
116093
+ description: issue.description,
116094
+ labels: labels ? labels.map((l) => l.name) : [],
116095
+ comments: comments ? await Promise.all(
116096
+ comments.map(async (c) => ({
116097
+ body: c.body,
116098
+ user: (await c.user)?.name || "Unknown",
116099
+ createdAt: c.createdAt
116100
+ }))
116101
+ ) : []
116102
+ };
116103
+ }
116104
+ return baseData;
116105
+ }
116106
+ )
115478
116107
  );
115479
116108
  ctx.output.data(
115480
116109
  JSON.stringify(
@@ -115522,7 +116151,14 @@ async function listIssues(ctx, options = {}) {
115522
116151
  ctx.output.data("");
115523
116152
  return;
115524
116153
  }
115525
- for (const { issue, state, assignee, team } of issuesWithDetails) {
116154
+ for (const {
116155
+ issue,
116156
+ state,
116157
+ assignee,
116158
+ team,
116159
+ labels,
116160
+ comments
116161
+ } of issuesWithDetails) {
115526
116162
  const emoji = PRIORITY_EMOJI2[issue.priority] || "\u26AA";
115527
116163
  const assigneeName = assignee ? `@${assignee.name}` : "";
115528
116164
  const teamBadge = team ? `[${team.key}]` : "";
@@ -115533,6 +116169,19 @@ async function listIssues(ctx, options = {}) {
115533
116169
  ctx.output.data(
115534
116170
  ` Status: ${state?.name || "unknown"}${assigneeName ? ` | Assignee: ${assigneeName}` : ""}`
115535
116171
  );
116172
+ if (options.export) {
116173
+ if (issue.description) {
116174
+ ctx.output.data(` Description: ${issue.description}`);
116175
+ }
116176
+ if (labels && labels.length > 0) {
116177
+ ctx.output.data(
116178
+ ` Labels: ${labels.map((l) => l.name).join(", ")}`
116179
+ );
116180
+ }
116181
+ if (comments && comments.length > 0) {
116182
+ ctx.output.data(` Comments: ${comments.length}`);
116183
+ }
116184
+ }
115536
116185
  }
115537
116186
  ctx.output.data("");
115538
116187
  ctx.output.data(" Use `skill linear issue <ID> --json` for full details.");
@@ -115561,26 +116210,31 @@ async function listMyIssues(ctx, options = {}) {
115561
116210
  const limit2 = options.limit || 20;
115562
116211
  try {
115563
116212
  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"]
116213
+ const { viewer, issues } = await withSpinner(
116214
+ "Loading issues...",
116215
+ async () => {
116216
+ const viewer2 = await client.viewer;
116217
+ const filter4 = {
116218
+ assignee: { id: { eq: viewer2.id } },
116219
+ state: {
116220
+ type: {
116221
+ nin: ["canceled", "completed"]
116222
+ }
116223
+ }
116224
+ };
116225
+ if (options.state) {
116226
+ filter4.state = {
116227
+ ...filter4.state || {},
116228
+ name: { eqIgnoreCase: options.state }
116229
+ };
115570
116230
  }
116231
+ const response = await client.issues({
116232
+ first: limit2,
116233
+ filter: filter4
116234
+ });
116235
+ return { viewer: viewer2, issues: response.nodes || [] };
115571
116236
  }
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 || [];
116237
+ );
115584
116238
  const issuesWithDetails = await Promise.all(
115585
116239
  issues.map(async (issue) => ({
115586
116240
  issue,
@@ -115759,9 +116413,12 @@ async function searchIssues(ctx, query, options = {}) {
115759
116413
  const limit2 = options.limit || 20;
115760
116414
  try {
115761
116415
  const client = getLinearClient();
115762
- const response = await client.searchIssues(query.trim(), {
115763
- first: limit2
115764
- });
116416
+ const response = await withSpinner(
116417
+ "Searching issues...",
116418
+ () => client.searchIssues(query.trim(), {
116419
+ first: limit2
116420
+ })
116421
+ );
115765
116422
  const issues = response.nodes || [];
115766
116423
  const issuesWithDetails = await Promise.all(
115767
116424
  issues.map(async (issue) => ({
@@ -116050,7 +116707,7 @@ init_esm_shims();
116050
116707
  async function listTeams(ctx, options = {}) {
116051
116708
  try {
116052
116709
  const client = getLinearClient();
116053
- const response = await client.teams();
116710
+ const response = await withSpinner("Loading teams...", () => client.teams());
116054
116711
  const teams = response.nodes || [];
116055
116712
  if (ctx.format === "json") {
116056
116713
  ctx.output.data(
@@ -116318,6 +116975,13 @@ Quick start:
116318
116975
  skill linear create "Title" Create issue
116319
116976
  skill linear search "query" Search issues
116320
116977
 
116978
+ Bulk queries:
116979
+ skill linear issues --older-than 90d Stale issues
116980
+ skill linear issues --older-than 2w --export Full data export
116981
+
116982
+ Environment:
116983
+ LINEAR_API_KEY required. Run 'skill doctor' to check.
116984
+
116321
116985
  All commands support --json for machine-readable output.
116322
116986
 
116323
116987
  \u26A0\uFE0F Write operations require personal LINEAR_API_KEY (run 'skill keys add').`
@@ -116330,8 +116994,10 @@ Examples:
116330
116994
  skill linear issues --team ENG Filter by team
116331
116995
  skill linear issues --state "In Progress" Filter by state
116332
116996
  skill linear issues --assignee me Your issues
116333
- skill linear issues --priority 0 Urgent only`
116334
- ).option("--limit <number>", "Maximum results (default: 20)", "20").option("--team <key>", "Filter by team key (e.g., ENG)").option("--state <name>", "Filter by state name").option("--assignee <email>", 'Filter by assignee (or "me")').option("--project <name>", "Filter by project name").option("--priority <0-4>", "Filter by priority (0=urgent)").option("--json", "Output as JSON with HATEOAS links").action(async (options, command) => {
116997
+ skill linear issues --priority 0 Urgent only
116998
+ skill linear issues --older-than 90d Stale issues (90+ days)
116999
+ skill linear issues --older-than 2w --export Full export`
117000
+ ).option("--limit <number>", "Maximum results (default: 20)", "20").option("--team <key>", "Filter by team key (e.g., ENG)").option("--state <name>", "Filter by state name").option("--assignee <email>", 'Filter by assignee (or "me")').option("--project <name>", "Filter by project name").option("--priority <0-4>", "Filter by priority (0=urgent)").option("--older-than <time>", "Filter by last update (90d, 2w, 24h, 3m)").option("--export", "Include full details (description, labels, comments)").option("--json", "Output as JSON with HATEOAS links").action(async (options, command) => {
116335
117001
  const ctx = await contextFromCommand2(command, options);
116336
117002
  await listIssues(ctx, {
116337
117003
  limit: parseInt(options.limit || "20", 10),
@@ -116339,7 +117005,9 @@ Examples:
116339
117005
  state: options.state,
116340
117006
  assignee: options.assignee,
116341
117007
  project: options.project,
116342
- priority: options.priority !== void 0 ? parseInt(options.priority, 10) : void 0
117008
+ priority: options.priority !== void 0 ? parseInt(options.priority, 10) : void 0,
117009
+ olderThan: options.olderThan,
117010
+ export: options.export
116343
117011
  });
116344
117012
  });
116345
117013
  linear.command("my").description(
@@ -116586,6 +117254,101 @@ Examples:
116586
117254
  });
116587
117255
  }
116588
117256
 
117257
+ // src/commands/list.ts
117258
+ init_esm_shims();
117259
+ import { existsSync as existsSync16, readFileSync as readFileSync13, readdirSync, statSync } from "fs";
117260
+ import { join as join14 } from "path";
117261
+ function discoverSkills(skillsDir) {
117262
+ if (!existsSync16(skillsDir)) {
117263
+ return [];
117264
+ }
117265
+ const skills = [];
117266
+ try {
117267
+ const entries = readdirSync(skillsDir);
117268
+ for (const entry of entries) {
117269
+ const entryPath = join14(skillsDir, entry);
117270
+ const stat = statSync(entryPath);
117271
+ if (!stat.isDirectory()) continue;
117272
+ const skillPath = join14(entryPath, "SKILL.md");
117273
+ if (!existsSync16(skillPath)) continue;
117274
+ const content = readFileSync13(skillPath, "utf8");
117275
+ const description = extractDescription(content);
117276
+ skills.push({
117277
+ name: entry,
117278
+ description,
117279
+ path: skillPath
117280
+ });
117281
+ }
117282
+ } catch {
117283
+ return [];
117284
+ }
117285
+ return skills.sort((a, b) => a.name.localeCompare(b.name));
117286
+ }
117287
+ function extractDescription(markdown) {
117288
+ const lines = markdown.split("\n");
117289
+ let inFrontmatter = false;
117290
+ let foundHeading = false;
117291
+ const descriptionLines = [];
117292
+ for (const line of lines) {
117293
+ const trimmed = line.trim();
117294
+ if (trimmed === "---") {
117295
+ if (!inFrontmatter && !foundHeading) {
117296
+ inFrontmatter = true;
117297
+ continue;
117298
+ } else if (inFrontmatter) {
117299
+ inFrontmatter = false;
117300
+ continue;
117301
+ }
117302
+ }
117303
+ if (inFrontmatter) continue;
117304
+ if (trimmed.startsWith("# ")) {
117305
+ foundHeading = true;
117306
+ continue;
117307
+ }
117308
+ if (foundHeading && trimmed.startsWith("#")) {
117309
+ break;
117310
+ }
117311
+ if (foundHeading) {
117312
+ if (trimmed === "") {
117313
+ if (descriptionLines.length > 0) {
117314
+ break;
117315
+ }
117316
+ continue;
117317
+ }
117318
+ descriptionLines.push(trimmed);
117319
+ }
117320
+ }
117321
+ return descriptionLines.join(" ").trim();
117322
+ }
117323
+ async function listAction(options, command) {
117324
+ const ctx = await createContext({
117325
+ format: options.json ? "json" : command.optsWithGlobals().format,
117326
+ verbose: command.optsWithGlobals().verbose,
117327
+ quiet: command.optsWithGlobals().quiet
117328
+ });
117329
+ const skillsDir = join14(process.cwd(), ".claude", "skills");
117330
+ const skills = discoverSkills(skillsDir);
117331
+ if (options.json) {
117332
+ ctx.output.data({ skills });
117333
+ return;
117334
+ }
117335
+ if (skills.length === 0) {
117336
+ ctx.output.message("No skills found in .claude/skills/");
117337
+ return;
117338
+ }
117339
+ ctx.output.data(`
117340
+ \u{1F4DA} Available Skills (${skills.length})`);
117341
+ ctx.output.data("\u2500".repeat(80));
117342
+ const rows = skills.map((skill) => ({
117343
+ Name: skill.name,
117344
+ Description: skill.description || "(no description)"
117345
+ }));
117346
+ ctx.output.table(rows);
117347
+ }
117348
+ function registerListCommand(program3) {
117349
+ program3.command("list").description("List available CLI skills for programmatic discovery").option("--json", "Output as JSON").action(listAction);
117350
+ }
117351
+
116589
117352
  // src/commands/memory/index.ts
116590
117353
  init_esm_shims();
116591
117354
 
@@ -117604,7 +118367,7 @@ var handlePipelineError = (ctx, error, message, suggestion = "Verify inputs and
117604
118367
  async function runPipelineCommand(ctx, opts) {
117605
118368
  const outputJson = opts.json === true || ctx.format === "json";
117606
118369
  try {
117607
- const { runPipeline: runPipeline2 } = await import("./pipeline-FGI6ICWM.js");
118370
+ const { runPipeline: runPipeline2 } = await import("./pipeline-TMFQSA7X.js");
117608
118371
  const result = await runPipeline2({
117609
118372
  message: {
117610
118373
  subject: opts.subject,
@@ -117715,16 +118478,16 @@ function registerPipelineCommands(program3) {
117715
118478
  // src/commands/plugin-sync.ts
117716
118479
  init_esm_shims();
117717
118480
  import { homedir as homedir3 } from "os";
117718
- import { dirname as dirname3, join as join13, resolve as resolve6 } from "path";
118481
+ import { dirname as dirname3, join as join15, resolve as resolve6 } from "path";
117719
118482
  import { fileURLToPath } from "url";
117720
118483
  var PLUGIN_SOURCE_DIR = resolve6(
117721
118484
  dirname3(fileURLToPath(import.meta.url)),
117722
118485
  "../../plugin"
117723
118486
  );
117724
- var PLUGIN_MANIFEST_RELATIVE = join13(".claude-plugin", "plugin.json");
118487
+ var PLUGIN_MANIFEST_RELATIVE = join15(".claude-plugin", "plugin.json");
117725
118488
  var resolveTargetDir = (global2) => {
117726
- const base = join13(homedir3(), ".claude", global2 ? "skills" : "plugins");
117727
- return join13(base, "skill-cli");
118489
+ const base = join15(homedir3(), ".claude", global2 ? "skills" : "plugins");
118490
+ return join15(base, "skill-cli");
117728
118491
  };
117729
118492
  var readManifest = async (path) => {
117730
118493
  const manifest = await readJson(path);
@@ -117753,7 +118516,7 @@ var writeResult4 = (ctx, payload) => {
117753
118516
  };
117754
118517
  async function executePluginSync(ctx, options) {
117755
118518
  try {
117756
- const sourceManifestPath = join13(PLUGIN_SOURCE_DIR, PLUGIN_MANIFEST_RELATIVE);
118519
+ const sourceManifestPath = join15(PLUGIN_SOURCE_DIR, PLUGIN_MANIFEST_RELATIVE);
117757
118520
  const sourceExists = await pathExists(sourceManifestPath);
117758
118521
  if (!sourceExists) {
117759
118522
  throw new CLIError({
@@ -117763,7 +118526,7 @@ async function executePluginSync(ctx, options) {
117763
118526
  }
117764
118527
  const sourceManifest = await readManifest(sourceManifestPath);
117765
118528
  const targetDir = resolveTargetDir(options.global);
117766
- const targetManifestPath = join13(targetDir, PLUGIN_MANIFEST_RELATIVE);
118529
+ const targetManifestPath = join15(targetDir, PLUGIN_MANIFEST_RELATIVE);
117767
118530
  const targetExists = await pathExists(targetManifestPath);
117768
118531
  if (targetExists && !options.force) {
117769
118532
  const targetManifest = await readManifest(targetManifestPath);
@@ -119006,7 +119769,7 @@ init_esm_shims();
119006
119769
  import { spawn } from "child_process";
119007
119770
  import { writeFile as writeFile7 } from "fs/promises";
119008
119771
  import { homedir as homedir4 } from "os";
119009
- import { dirname as dirname4, join as join14 } from "path";
119772
+ import { dirname as dirname4, join as join16 } from "path";
119010
119773
  var CONFIG_DIR_NAME = "skill-cli";
119011
119774
  var AUTO_UPDATE_STATE_FILE = "auto-update.json";
119012
119775
  var DEFAULT_PACKAGE = "@skillrecordings/cli";
@@ -119018,7 +119781,7 @@ var AutoUpdateStore = class {
119018
119781
  now;
119019
119782
  constructor(options = {}) {
119020
119783
  const configDir = resolveConfigDir(options.configDir);
119021
- this.filePath = join14(configDir, AUTO_UPDATE_STATE_FILE);
119784
+ this.filePath = join16(configDir, AUTO_UPDATE_STATE_FILE);
119022
119785
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
119023
119786
  }
119024
119787
  getNow() {
@@ -119046,9 +119809,9 @@ function resolveConfigDir(configDir) {
119046
119809
  if (configDir) return configDir;
119047
119810
  const xdgConfigHome = process.env.XDG_CONFIG_HOME;
119048
119811
  if (xdgConfigHome && xdgConfigHome.trim() !== "") {
119049
- return join14(xdgConfigHome, CONFIG_DIR_NAME);
119812
+ return join16(xdgConfigHome, CONFIG_DIR_NAME);
119050
119813
  }
119051
- return join14(homedir4(), ".config", CONFIG_DIR_NAME);
119814
+ return join16(homedir4(), ".config", CONFIG_DIR_NAME);
119052
119815
  }
119053
119816
  function isAutoUpdateState(value) {
119054
119817
  if (!value || typeof value !== "object") return false;
@@ -119220,9 +119983,6 @@ init_esm_shims();
119220
119983
  var DEFAULT_MAX_HINTS = 2;
119221
119984
  var getCommandCount = (state, command) => state.commands[command]?.count ?? 0;
119222
119985
  var hasCommand = (state, command) => getCommandCount(state, command) > 0;
119223
- var hasCommandPrefix = (state, prefix) => Object.entries(state.commands).some(
119224
- ([name, entry]) => name.startsWith(prefix) && entry.count > 0
119225
- );
119226
119986
  var hasMilestone = (state, milestone) => state.milestones[milestone]?.achieved ?? false;
119227
119987
  var shouldSuppressHints = (context) => context.quiet === true || context.format === "json";
119228
119988
  var resolveMaxHints = (context) => context.maxHints ?? DEFAULT_MAX_HINTS;
@@ -119231,79 +119991,76 @@ var toHint = (rule) => ({
119231
119991
  message: rule.message,
119232
119992
  audience: rule.audience
119233
119993
  });
119994
+ var isCommandGroup = (command, group) => command === group || command.startsWith(`${group}.`);
119234
119995
  var DEFAULT_HINT_RULES = [
119996
+ // Front contextual hints
119235
119997
  {
119236
- id: "onboarding.wizard",
119237
- audience: "onboarding",
119238
- message: "New here? Run `skill wizard` to set up your first product.",
119239
- showWhen: (state) => state.totalRuns <= 2 && !hasCommand(state, "wizard"),
119240
- retireWhen: (state) => hasCommand(state, "wizard") || hasMilestone(state, "wizard_completed")
119241
- },
119242
- {
119243
- id: "onboarding.auth",
119244
- audience: "onboarding",
119245
- message: "Set up your own API keys with `skill keys`.",
119246
- showWhen: (state) => state.totalRuns >= 1 && !hasMilestone(state, "auth_configured"),
119247
- retireWhen: (state) => hasMilestone(state, "auth_configured")
119248
- },
119249
- {
119250
- id: "discovery.health",
119251
- audience: "discovery",
119252
- message: "Check integrations fast with `skill health <app-slug>`.",
119253
- showWhen: (state) => state.totalRuns >= 2 && !hasCommand(state, "health"),
119254
- retireWhen: (state) => hasCommand(state, "health")
119998
+ id: "context.front.triage",
119999
+ audience: "contextual",
120000
+ postRun: true,
120001
+ message: "Tip: `skill front triage` to auto-categorize unassigned threads.",
120002
+ showWhen: (state, context) => context.command === "front.inbox" && !hasCommand(state, "front.triage"),
120003
+ retireWhen: (state) => hasCommand(state, "front.triage")
119255
120004
  },
119256
120005
  {
119257
- id: "discovery.front.inbox",
119258
- audience: "discovery",
119259
- message: "List recent conversations via `skill front inbox <name-or-id>`.",
119260
- showWhen: (state) => state.totalRuns >= 1 && !hasCommand(state, "front.inbox"),
119261
- retireWhen: (state) => hasCommand(state, "front.inbox")
120006
+ id: "context.front.conversation",
120007
+ audience: "contextual",
120008
+ postRun: true,
120009
+ message: "Tip: `skill front conversation <id> -m` shows the full thread.",
120010
+ showWhen: (state, context) => context.command === "front.message" && !hasCommand(state, "front.conversation"),
120011
+ retireWhen: (state) => hasCommand(state, "front.conversation")
119262
120012
  },
119263
120013
  {
119264
- id: "discovery.inngest",
119265
- audience: "discovery",
119266
- message: "Inspect workflows with `skill inngest stats --after 1d`.",
119267
- showWhen: (state) => state.totalRuns >= 3 && !hasCommandPrefix(state, "inngest."),
119268
- retireWhen: (state) => hasCommandPrefix(state, "inngest.")
120014
+ id: "context.front.reply",
120015
+ audience: "contextual",
120016
+ postRun: true,
120017
+ message: "Tip: `skill front reply <id>` to draft a response.",
120018
+ showWhen: (state, context) => context.command === "front.conversation" && !hasCommand(state, "front.reply"),
120019
+ retireWhen: (state) => hasCommand(state, "front.reply")
119269
120020
  },
120021
+ // Inngest contextual hints
119270
120022
  {
119271
- id: "discovery.axiom",
119272
- audience: "discovery",
119273
- message: 'Query logs quickly with `skill axiom query "<APL>" --since 24h`.',
119274
- showWhen: (state) => state.totalRuns >= 3 && !hasCommandPrefix(state, "axiom."),
119275
- retireWhen: (state) => hasCommandPrefix(state, "axiom.")
120023
+ id: "context.inngest.run",
120024
+ audience: "contextual",
120025
+ postRun: true,
120026
+ message: "Tip: `skill inngest run <id>` to inspect a specific run.",
120027
+ showWhen: (state, context) => (context.command === "inngest.failures" || context.command === "inngest.runs") && !hasCommand(state, "inngest.run"),
120028
+ retireWhen: (state) => hasCommand(state, "inngest.run")
119276
120029
  },
119277
120030
  {
119278
- id: "discovery.keys",
119279
- audience: "discovery",
119280
- message: "Override shared credentials with your own: `skill keys add`",
119281
- showWhen: (state) => state.totalRuns >= 3 && !hasCommand(state, "keys") && !hasMilestone(state, "auth_configured"),
119282
- retireWhen: (state) => hasCommand(state, "keys") || hasCommand(state, "keys.add") || hasMilestone(state, "auth_configured")
120031
+ id: "context.inngest.trace",
120032
+ audience: "contextual",
120033
+ postRun: true,
120034
+ message: "Tip: `skill inngest trace <run-id>` for full workflow trace.",
120035
+ showWhen: (state, context) => context.command === "inngest.run" && !hasCommand(state, "inngest.trace"),
120036
+ retireWhen: (state) => hasCommand(state, "inngest.trace")
119283
120037
  },
120038
+ // Linear contextual hints
119284
120039
  {
119285
- id: "context.front.triage",
120040
+ id: "context.linear.my",
119286
120041
  audience: "contextual",
119287
120042
  postRun: true,
119288
- message: "Tip: `skill front triage <inbox-id>` surfaces unassigned threads.",
119289
- showWhen: (state, context) => context.command === "front.inbox" && !hasCommand(state, "front.triage"),
119290
- retireWhen: (state) => hasCommand(state, "front.triage")
120043
+ message: "Tip: `skill linear my` to see your assigned issues.",
120044
+ showWhen: (state, context) => isCommandGroup(context.command, "linear") && context.command !== "linear.my" && !hasCommand(state, "linear.my"),
120045
+ retireWhen: (state) => hasCommand(state, "linear.my")
119291
120046
  },
120047
+ // Axiom contextual hints
119292
120048
  {
119293
- id: "context.front.conversation",
120049
+ id: "context.axiom.errors",
119294
120050
  audience: "contextual",
119295
120051
  postRun: true,
119296
- message: "Tip: `skill front conversation <id> -m` shows the full thread.",
119297
- showWhen: (state, context) => context.command === "front.message" && !hasCommand(state, "front.conversation"),
119298
- retireWhen: (state) => hasCommand(state, "front.conversation")
120052
+ message: "Tip: `skill axiom errors --since 1h` to see recent errors.",
120053
+ showWhen: (state, context) => context.command === "axiom.query" && !hasCommand(state, "axiom.errors"),
120054
+ retireWhen: (state) => hasCommand(state, "axiom.errors")
119299
120055
  },
120056
+ // Keys hint - only when auth issues are likely
119300
120057
  {
119301
- id: "context.inngest.run",
120058
+ id: "context.keys",
119302
120059
  audience: "contextual",
119303
120060
  postRun: true,
119304
- message: "Tip: drill in with `skill inngest run <id>` or `skill inngest trace <run-id>`.",
119305
- showWhen: (state, context) => context.command === "inngest.failures" && !hasCommand(state, "inngest.run") && !hasCommand(state, "inngest.trace"),
119306
- retireWhen: (state) => hasCommand(state, "inngest.run") || hasCommand(state, "inngest.trace")
120061
+ message: "Tip: `skill keys setup` to configure keychain integration.",
120062
+ showWhen: (state, context) => context.command === "auth.status" && !hasCommand(state, "keys.setup") && !hasMilestone(state, "auth_configured"),
120063
+ retireWhen: (state) => hasCommand(state, "keys.setup") || hasMilestone(state, "auth_configured")
119307
120064
  }
119308
120065
  ];
119309
120066
  var HintEngine = class {
@@ -119351,13 +120108,13 @@ var writeHints = (hints, stderr) => {
119351
120108
  init_esm_shims();
119352
120109
  import { lstat, readlink, symlink } from "fs/promises";
119353
120110
  import { homedir as homedir5 } from "os";
119354
- import { dirname as dirname5, join as join15, resolve as resolve7 } from "path";
120111
+ import { dirname as dirname5, join as join17, resolve as resolve7 } from "path";
119355
120112
  import { fileURLToPath as fileURLToPath2 } from "url";
119356
120113
  var SKILL_SOURCE_DIR = resolve7(
119357
120114
  dirname5(fileURLToPath2(import.meta.url)),
119358
120115
  "../../../../.claude/skills/skill-cli"
119359
120116
  );
119360
- var SKILL_TARGET_DIR = join15(homedir5(), ".claude", "skills", "skill-cli");
120117
+ var SKILL_TARGET_DIR = join17(homedir5(), ".claude", "skills", "skill-cli");
119361
120118
  async function autoLinkSkill() {
119362
120119
  const source = SKILL_SOURCE_DIR;
119363
120120
  const target = SKILL_TARGET_DIR;
@@ -119461,16 +120218,16 @@ async function sendTelemetryEvent(event) {
119461
120218
  init_esm_shims();
119462
120219
  import { writeFile as writeFile8 } from "fs/promises";
119463
120220
  import { homedir as homedir6 } from "os";
119464
- import { dirname as dirname6, join as join16 } from "path";
120221
+ import { dirname as dirname6, join as join18 } from "path";
119465
120222
  var CONFIG_DIR_NAME2 = "skill-cli";
119466
120223
  var USAGE_FILE_NAME = "usage.json";
119467
120224
  function resolveConfigDir2(configDir) {
119468
120225
  if (configDir) return configDir;
119469
120226
  const xdgConfigHome = process.env.XDG_CONFIG_HOME;
119470
120227
  if (xdgConfigHome && xdgConfigHome.trim() !== "") {
119471
- return join16(xdgConfigHome, CONFIG_DIR_NAME2);
120228
+ return join18(xdgConfigHome, CONFIG_DIR_NAME2);
119472
120229
  }
119473
- return join16(homedir6(), ".config", CONFIG_DIR_NAME2);
120230
+ return join18(homedir6(), ".config", CONFIG_DIR_NAME2);
119474
120231
  }
119475
120232
  function createDefaultState(now) {
119476
120233
  return {
@@ -119508,7 +120265,7 @@ var UsageTracker = class {
119508
120265
  statePromise;
119509
120266
  constructor(options = {}) {
119510
120267
  const configDir = resolveConfigDir2(options.configDir);
119511
- this.filePath = join16(configDir, USAGE_FILE_NAME);
120268
+ this.filePath = join18(configDir, USAGE_FILE_NAME);
119512
120269
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
119513
120270
  }
119514
120271
  async loadState() {
@@ -120087,8 +120844,8 @@ if (!envLoaded && !process.env.DATABASE_URL) {
120087
120844
  process.env.SKIP_ENV_VALIDATION = "1";
120088
120845
  }
120089
120846
  var runtimeTarget = `bun-${process.platform}-${process.arch}`;
120090
- var buildVersion = "0.16.1".length > 0 ? "0.16.1" : "0.0.0-dev";
120091
- var buildCommit = "7f0ef62".length > 0 ? "7f0ef62" : "dev";
120847
+ var buildVersion = "0.18.0".length > 0 ? "0.18.0" : "0.0.0-dev";
120848
+ var buildCommit = "8d98b14".length > 0 ? "8d98b14" : "dev";
120092
120849
  var buildTarget = "node".length > 0 ? "node" : runtimeTarget;
120093
120850
  var isDevBuild = buildVersion.includes("dev") || buildCommit === "dev";
120094
120851
  var versionLabel = `skill v${buildVersion} (${buildCommit}) ${buildTarget}`;
@@ -120102,7 +120859,6 @@ var usageState = await (async () => {
120102
120859
  return null;
120103
120860
  }
120104
120861
  })();
120105
- var hintCounts = /* @__PURE__ */ new WeakMap();
120106
120862
  var commandStartTimes = /* @__PURE__ */ new WeakMap();
120107
120863
  var resolveCommandName = (command) => {
120108
120864
  const names = [];
@@ -120158,16 +120914,6 @@ program2.hook("preAction", (thisCommand, actionCommand) => {
120158
120914
  program2.hook("preAction", (_thisCommand, actionCommand) => {
120159
120915
  commandStartTimes.set(actionCommand, Date.now());
120160
120916
  });
120161
- program2.hook("preAction", async (_thisCommand, actionCommand) => {
120162
- try {
120163
- const context = resolveHintContext(actionCommand);
120164
- const state = await usageTracker.getUsage();
120165
- const hints = hintEngine.getHints(state, context);
120166
- writeHints(hints, process.stderr);
120167
- hintCounts.set(actionCommand, hints.length);
120168
- } catch {
120169
- }
120170
- });
120171
120917
  program2.hook("postAction", async (_thisCommand, actionCommand) => {
120172
120918
  try {
120173
120919
  const context = resolveHintContext(actionCommand);
@@ -120176,11 +120922,7 @@ program2.hook("postAction", async (_thisCommand, actionCommand) => {
120176
120922
  for (const milestone of milestones) {
120177
120923
  await usageTracker.setMilestone(milestone);
120178
120924
  }
120179
- const previouslyShown = hintCounts.get(actionCommand) ?? 0;
120180
- const postHint = hintEngine.getPostRunHint(state, {
120181
- ...context,
120182
- previouslyShown
120183
- });
120925
+ const postHint = hintEngine.getPostRunHint(state, context);
120184
120926
  if (postHint) writeHints([postHint], process.stderr);
120185
120927
  } catch {
120186
120928
  }
@@ -120308,6 +121050,8 @@ registerKbCommands(program2);
120308
121050
  registerAuthCommands(program2, usageState);
120309
121051
  registerConfigCommands(program2);
120310
121052
  registerKeysCommands(program2);
121053
+ registerDoctorCommand(program2);
121054
+ registerListCommand(program2);
120311
121055
  registerPluginSyncCommand(program2);
120312
121056
  program2.command("mcp").description(
120313
121057
  "Start MCP server for AI coding agent integration.\n Exposes 9 Front tools over JSON-RPC stdio for Claude Code, Cursor, etc.\n Tools: inbox, conversation, message, assign, reply, tag, archive, search, report\n Usage: skill mcp (then connect your AI editor to stdin/stdout)"