@skillrecordings/cli 0.17.0 → 0.18.1

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,7 +20,7 @@ import {
20
20
  runPipeline,
21
21
  validate,
22
22
  validateSync
23
- } from "./chunk-J4IC3GC3.js";
23
+ } from "./chunk-ZCWCT4ES.js";
24
24
  import "./chunk-HK3PEWFD.js";
25
25
  import "./chunk-WYKL32C3.js";
26
26
  import {
@@ -40,7 +40,7 @@ import {
40
40
  getApp,
41
41
  getOutcomeHistory,
42
42
  getRedis
43
- } from "./chunk-IR6KG25Y.js";
43
+ } from "./chunk-LV53JZHC.js";
44
44
  import "./chunk-KEV3QKXP.js";
45
45
  import {
46
46
  MemoryService,
@@ -56,8 +56,10 @@ import "./chunk-F4EM72IH.js";
56
56
  import {
57
57
  addShellIntegration,
58
58
  autoBootstrapKeychain,
59
+ getFromKeychain,
59
60
  getKeychainStatus,
60
61
  isKeychainSupported,
62
+ isOpCliAvailable,
61
63
  storeInKeychain
62
64
  } from "./chunk-7SQU7KCI.js";
63
65
  import {
@@ -74553,7 +74555,7 @@ import { resolve as resolve8 } from "path";
74553
74555
 
74554
74556
  // src/core/config-loader.ts
74555
74557
  init_esm_shims();
74556
- import { readFileSync } from "fs";
74558
+ import { existsSync as existsSync2, readFileSync } from "fs";
74557
74559
  import { resolve } from "path";
74558
74560
 
74559
74561
  // src/core/user-config.ts
@@ -74601,6 +74603,13 @@ function getUserConfigDir(configDir) {
74601
74603
  function getUserConfigPath(fileName, configDir) {
74602
74604
  return join(getUserConfigDir(configDir), fileName);
74603
74605
  }
74606
+ function getAgeKeyPath(configDir) {
74607
+ const envKey = process.env.AGE_USER_KEY;
74608
+ if (envKey && envKey.trim() !== "") {
74609
+ return envKey;
74610
+ }
74611
+ return getUserConfigPath(USER_CONFIG_PATHS.ageKey, configDir);
74612
+ }
74604
74613
 
74605
74614
  // src/core/config-loader.ts
74606
74615
  function parseEnvContent(content) {
@@ -74618,12 +74627,11 @@ function parseEnvContent(content) {
74618
74627
  return env;
74619
74628
  }
74620
74629
  async function decryptEnvFile(encryptedPath) {
74621
- const { existsSync: existsSync15 } = await import("fs");
74622
74630
  const { readFile: readFile10 } = await import("fs/promises");
74623
- if (!existsSync15(encryptedPath)) {
74631
+ if (!existsSync2(encryptedPath)) {
74624
74632
  return {};
74625
74633
  }
74626
- const ageKey = await getAgeKeyFrom1Password();
74634
+ const ageKey = await getAgeKey();
74627
74635
  if (!ageKey) {
74628
74636
  return {};
74629
74637
  }
@@ -74636,13 +74644,21 @@ async function decryptEnvFile(encryptedPath) {
74636
74644
  return {};
74637
74645
  }
74638
74646
  }
74639
- async function getAgeKeyFrom1Password() {
74647
+ async function getAgeKey() {
74640
74648
  if (process.env.SKILL_AGE_KEY) {
74641
74649
  return process.env.SKILL_AGE_KEY;
74642
74650
  }
74643
74651
  try {
74644
- const { getFromKeychain, storeInKeychain: storeInKeychain2, autoBootstrapKeychain: autoBootstrapKeychain2 } = await import("./keychain-IEZHT5WN.js");
74645
- const fromKeychain = getFromKeychain("age-private-key");
74652
+ const ageKeyPath = getAgeKeyPath();
74653
+ if (existsSync2(ageKeyPath)) {
74654
+ const key = readFileSync(ageKeyPath, "utf8").trim();
74655
+ if (key) return key;
74656
+ }
74657
+ } catch {
74658
+ }
74659
+ try {
74660
+ const { getFromKeychain: getFromKeychain2, storeInKeychain: storeInKeychain2, autoBootstrapKeychain: autoBootstrapKeychain2 } = await import("./keychain-IEZHT5WN.js");
74661
+ const fromKeychain = getFromKeychain2("age-private-key");
74646
74662
  if (fromKeychain) return fromKeychain;
74647
74663
  let opToken = process.env.OP_SERVICE_ACCOUNT_TOKEN;
74648
74664
  if (!opToken) {
@@ -74750,7 +74766,7 @@ var ROOT_DESCRIPTIONS = {
74750
74766
  minimal: "Skill Recordings support agent CLI. Try: skill wizard, skill keys, skill front inbox, skill front triage. Use --help for details."
74751
74767
  };
74752
74768
  var FRONT_DESCRIPTIONS = {
74753
- full: "Front conversations, inboxes, tags, archival, and reporting.\n\n Start here:\n skill front inbox See unassigned conversations\n skill front inbox support List conversations in a specific inbox\n skill front triage AI-powered categorization of inbox items\n\n Investigate a conversation:\n skill front conversation <id> -m Full conversation with messages\n skill front message <id> Single message details + body\n\n Take action:\n skill front assign <id> Assign to a teammate\n skill front reply <id> Draft a reply (HITL, never auto-sends)\n skill front tag <id> Add a tag\n skill front archive <id> Archive a resolved conversation\n\n Bulk operations:\n skill front bulk-archive Archive old/spam conversations\n skill front report Volume + tag + sender forensics\n\n All commands accept --json for HATEOAS-enriched output with _links and _actions.",
74769
+ 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.",
74754
74770
  abbreviated: "Front API workflows for inbox triage and conversation actions.\n\n Common:\n skill front inbox\n skill front triage\n skill front conversation <id> -m\n skill front reply <id>\n skill front archive <id>",
74755
74771
  minimal: "Front API commands (inbox, triage, assign, reply, archive)."
74756
74772
  };
@@ -74760,7 +74776,7 @@ var AUTH_DESCRIPTIONS = {
74760
74776
  minimal: "Auth status commands (auth status, auth whoami)."
74761
74777
  };
74762
74778
  var INNGEST_DESCRIPTIONS = {
74763
- full: "Inngest event and workflow commands.\n\n Debug pipeline runs:\n skill inngest runs --status failed --after 1h Recent failures\n skill inngest events --after 12h Recent events\n skill inngest investigate <run-id> Deep-dive a specific run",
74779
+ 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.",
74764
74780
  abbreviated: "Inngest events and workflow runs.\n\n Common:\n skill inngest runs --status failed --after 1h\n skill inngest events --after 12h\n skill inngest investigate <run-id>",
74765
74781
  minimal: "Inngest events and runs debugging."
74766
74782
  };
@@ -75085,7 +75101,7 @@ async function loginAction(ctx, options) {
75085
75101
 
75086
75102
  // src/commands/auth/setup.ts
75087
75103
  init_esm_shims();
75088
- import { existsSync as existsSync2, writeFileSync } from "fs";
75104
+ import { existsSync as existsSync3, writeFileSync } from "fs";
75089
75105
  import { resolve as resolve2 } from "path";
75090
75106
  import { confirm, select } from "@inquirer/prompts";
75091
75107
 
@@ -75434,7 +75450,7 @@ async function authSetupAction(ctx, options = {}) {
75434
75450
  });
75435
75451
  if (persistence === "env") {
75436
75452
  const envPath = resolve2(process.cwd(), ".env.local");
75437
- if (existsSync2(envPath)) {
75453
+ if (existsSync3(envPath)) {
75438
75454
  const overwrite = await confirm({
75439
75455
  message: `.env.local already exists at ${envPath}. Overwrite?`,
75440
75456
  default: false
@@ -76836,7 +76852,9 @@ async function listApprovals(ctx, options) {
76836
76852
  }
76837
76853
  }
76838
76854
  function registerAxiomCommands(program3) {
76839
- const axiom = program3.command("axiom").description("Query Axiom logs and traces");
76855
+ const axiom = program3.command("axiom").description(
76856
+ "Query Axiom logs and traces.\n Environment: AXIOM_TOKEN required. Run `skill doctor` to check."
76857
+ );
76840
76858
  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) => {
76841
76859
  const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
76842
76860
  ...command.parent?.opts(),
@@ -77074,11 +77092,11 @@ Saved to ${options.output}`);
77074
77092
  }
77075
77093
  }
77076
77094
  async function toEvalite(options) {
77077
- const { readFileSync: readFileSync13 } = await import("fs");
77095
+ const { readFileSync: readFileSync14 } = await import("fs");
77078
77096
  const { ctx } = options;
77079
77097
  const outputJson = ctx.format === "json";
77080
77098
  const data2 = JSON.parse(
77081
- readFileSync13(options.input, "utf-8")
77099
+ readFileSync14(options.input, "utf-8")
77082
77100
  );
77083
77101
  const evaliteData = data2.map((d) => ({
77084
77102
  input: d.triggerMessage.body,
@@ -77139,12 +77157,12 @@ init_esm_shims();
77139
77157
 
77140
77158
  // src/commands/config/get.ts
77141
77159
  init_esm_shims();
77142
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
77160
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
77143
77161
  import { Decrypter as Decrypter2 } from "age-encryption";
77144
77162
 
77145
77163
  // src/commands/config/set.ts
77146
77164
  init_esm_shims();
77147
- import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
77165
+ import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
77148
77166
  import { password, select as select2 } from "@inquirer/prompts";
77149
77167
  import { Decrypter, Encrypter, identityToRecipient } from "age-encryption";
77150
77168
 
@@ -77155,7 +77173,7 @@ import { join as join2 } from "path";
77155
77173
  function getUserConfigDir2() {
77156
77174
  return join2(homedir2(), ".config", "skill");
77157
77175
  }
77158
- function getAgeKeyPath() {
77176
+ function getAgeKeyPath2() {
77159
77177
  return join2(getUserConfigDir2(), "age.key");
77160
77178
  }
77161
77179
  async function configInitAction(ctx, options = {}) {
@@ -77193,7 +77211,7 @@ function parseKeyValue(input2) {
77193
77211
  return { key, value };
77194
77212
  }
77195
77213
  async function readExistingConfig(identity, configPath) {
77196
- if (!existsSync3(configPath)) {
77214
+ if (!existsSync4(configPath)) {
77197
77215
  return {};
77198
77216
  }
77199
77217
  try {
@@ -77219,8 +77237,8 @@ async function readExistingConfig(identity, configPath) {
77219
77237
  }
77220
77238
  async function configSetAction(ctx, keyValue, options = {}) {
77221
77239
  const outputJson = options.json === true || ctx.format === "json";
77222
- const keyPath = getAgeKeyPath();
77223
- if (!existsSync3(keyPath)) {
77240
+ const keyPath = getAgeKeyPath2();
77241
+ if (!existsSync4(keyPath)) {
77224
77242
  const result = {
77225
77243
  success: false,
77226
77244
  error: "Age key not found. Run: skill config init"
@@ -77307,24 +77325,9 @@ async function configSetAction(ctx, keyValue, options = {}) {
77307
77325
  }
77308
77326
 
77309
77327
  // src/commands/config/get.ts
77310
- async function getAgeKeyFrom1Password2() {
77311
- if (!process.env.OP_SERVICE_ACCOUNT_TOKEN) {
77312
- return null;
77313
- }
77314
- try {
77315
- const { OnePasswordProvider: OnePasswordProvider2 } = await import("./secrets-MGVPGMFJ.js");
77316
- const op = new OnePasswordProvider2();
77317
- if (!await op.isAvailable()) {
77318
- return null;
77319
- }
77320
- return await op.resolve("op://Support/skill-cli-age-key/private_key");
77321
- } catch {
77322
- return null;
77323
- }
77324
- }
77325
77328
  async function decryptConfig(identity) {
77326
77329
  const configPath = getEncryptedConfigPath();
77327
- if (!existsSync4(configPath)) {
77330
+ if (!existsSync5(configPath)) {
77328
77331
  return {};
77329
77332
  }
77330
77333
  const encrypted = readFileSync3(configPath);
@@ -77352,11 +77355,11 @@ async function decryptConfig(identity) {
77352
77355
  }
77353
77356
  async function configGetAction(ctx, key, options = {}) {
77354
77357
  const outputJson = options.json === true || ctx.format === "json";
77355
- const identity = await getAgeKeyFrom1Password2();
77358
+ const identity = await getAgeKey();
77356
77359
  if (!identity) {
77357
77360
  const result = {
77358
77361
  success: false,
77359
- error: "OP_SERVICE_ACCOUNT_TOKEN not set. Required for encrypted config."
77362
+ error: "No age key found. Set SKILL_AGE_KEY, place key at ~/.config/skill/age.key, or configure 1Password."
77360
77363
  };
77361
77364
  if (outputJson) {
77362
77365
  ctx.output.data(result);
@@ -77409,26 +77412,11 @@ async function configGetAction(ctx, key, options = {}) {
77409
77412
 
77410
77413
  // src/commands/config/list.ts
77411
77414
  init_esm_shims();
77412
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
77415
+ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
77413
77416
  import { Decrypter as Decrypter3 } from "age-encryption";
77414
- async function getAgeKeyFrom1Password3() {
77415
- if (!process.env.OP_SERVICE_ACCOUNT_TOKEN) {
77416
- return null;
77417
- }
77418
- try {
77419
- const { OnePasswordProvider: OnePasswordProvider2 } = await import("./secrets-MGVPGMFJ.js");
77420
- const op = new OnePasswordProvider2();
77421
- if (!await op.isAvailable()) {
77422
- return null;
77423
- }
77424
- return await op.resolve("op://Support/skill-cli-age-key/private_key");
77425
- } catch {
77426
- return null;
77427
- }
77428
- }
77429
77417
  async function decryptConfig2(identity) {
77430
77418
  const configPath = getEncryptedConfigPath();
77431
- if (!existsSync5(configPath)) {
77419
+ if (!existsSync6(configPath)) {
77432
77420
  return {};
77433
77421
  }
77434
77422
  const encrypted = readFileSync4(configPath);
@@ -77456,11 +77444,11 @@ async function decryptConfig2(identity) {
77456
77444
  }
77457
77445
  async function configListAction(ctx, options = {}) {
77458
77446
  const outputJson = options.json === true || ctx.format === "json";
77459
- const identity = await getAgeKeyFrom1Password3();
77447
+ const identity = await getAgeKey();
77460
77448
  if (!identity) {
77461
77449
  const result = {
77462
77450
  success: false,
77463
- error: "OP_SERVICE_ACCOUNT_TOKEN not set. Required for encrypted config."
77451
+ error: "No age key found. Set SKILL_AGE_KEY, place key at ~/.config/skill/age.key, or configure 1Password."
77464
77452
  };
77465
77453
  if (outputJson) {
77466
77454
  ctx.output.data(result);
@@ -77882,6 +77870,170 @@ function registerDeployCommands(program3) {
77882
77870
  });
77883
77871
  }
77884
77872
 
77873
+ // src/commands/doctor.ts
77874
+ init_esm_shims();
77875
+ import { execSync as execSync3 } from "child_process";
77876
+ import { existsSync as existsSync7 } from "fs";
77877
+ import { join as join3 } from "path";
77878
+ var REQUIRED_ENV_VARS = [
77879
+ "DATABASE_URL",
77880
+ "INNGEST_SIGNING_KEY",
77881
+ "INNGEST_EVENT_KEY",
77882
+ "FRONT_API_TOKEN",
77883
+ "LINEAR_API_KEY",
77884
+ "AXIOM_TOKEN",
77885
+ "SLACK_BOT_TOKEN",
77886
+ "UPSTASH_REDIS_REST_URL",
77887
+ "UPSTASH_VECTOR_REST_URL"
77888
+ ];
77889
+ function checkEnvVars() {
77890
+ return REQUIRED_ENV_VARS.map((varName) => {
77891
+ const value = process.env[varName];
77892
+ return {
77893
+ name: varName,
77894
+ status: value ? "ok" : "warn",
77895
+ message: value ? void 0 : "Not set"
77896
+ };
77897
+ });
77898
+ }
77899
+ function checkKeychain() {
77900
+ const checks = [];
77901
+ const opAvailable = isOpCliAvailable();
77902
+ checks.push({
77903
+ name: "op CLI",
77904
+ status: opAvailable ? "ok" : "warn",
77905
+ message: opAvailable ? void 0 : "Not installed or not authenticated"
77906
+ });
77907
+ const opToken = getFromKeychain("op-service-account-token");
77908
+ checks.push({
77909
+ name: "op-service-account-token",
77910
+ status: opToken ? "ok" : "warn",
77911
+ message: opToken ? void 0 : "Not found in keychain"
77912
+ });
77913
+ const ageKey = getFromKeychain("age-private-key");
77914
+ checks.push({
77915
+ name: "age-private-key",
77916
+ status: ageKey ? "ok" : "warn",
77917
+ message: ageKey ? void 0 : "Not found in keychain"
77918
+ });
77919
+ return checks;
77920
+ }
77921
+ function checkTools() {
77922
+ const checks = [];
77923
+ try {
77924
+ execSync3("gh auth status", { stdio: "pipe", timeout: 3e3 });
77925
+ checks.push({
77926
+ name: "gh CLI",
77927
+ status: "ok"
77928
+ });
77929
+ } catch {
77930
+ checks.push({
77931
+ name: "gh CLI",
77932
+ status: "warn",
77933
+ message: "Not installed or not authenticated"
77934
+ });
77935
+ }
77936
+ return checks;
77937
+ }
77938
+ function checkWorkspace() {
77939
+ const checks = [];
77940
+ const hivePath = join3(process.cwd(), ".hive");
77941
+ const hiveExists = existsSync7(hivePath);
77942
+ checks.push({
77943
+ name: ".hive directory",
77944
+ status: hiveExists ? "ok" : "warn",
77945
+ message: hiveExists ? void 0 : "Not found in current directory"
77946
+ });
77947
+ return checks;
77948
+ }
77949
+ function runHealthChecks() {
77950
+ const categories = [
77951
+ {
77952
+ category: "Environment",
77953
+ checks: checkEnvVars()
77954
+ },
77955
+ {
77956
+ category: "Keychain",
77957
+ checks: checkKeychain()
77958
+ },
77959
+ {
77960
+ category: "Tools",
77961
+ checks: checkTools()
77962
+ },
77963
+ {
77964
+ category: "Workspace",
77965
+ checks: checkWorkspace()
77966
+ }
77967
+ ];
77968
+ let total = 0;
77969
+ let ok = 0;
77970
+ let warn = 0;
77971
+ let fail = 0;
77972
+ for (const category of categories) {
77973
+ for (const check of category.checks) {
77974
+ total++;
77975
+ if (check.status === "ok") ok++;
77976
+ else if (check.status === "warn") warn++;
77977
+ else fail++;
77978
+ }
77979
+ }
77980
+ let status;
77981
+ if (fail > 0) {
77982
+ status = "unhealthy";
77983
+ } else if (warn > 3) {
77984
+ status = "degraded";
77985
+ } else {
77986
+ status = "healthy";
77987
+ }
77988
+ return {
77989
+ status,
77990
+ categories,
77991
+ summary: { total, ok, warn, fail }
77992
+ };
77993
+ }
77994
+ function formatTextOutput(results) {
77995
+ console.log("\n\u{1FA7A} Health Check Results\n");
77996
+ for (const category of results.categories) {
77997
+ console.log(`${category.category}:`);
77998
+ for (const check of category.checks) {
77999
+ const symbol = check.status === "ok" ? "\u2713" : check.status === "warn" ? "\u26A0" : "\u2717";
78000
+ const line = check.message ? ` ${symbol} ${check.name} - ${check.message}` : ` ${symbol} ${check.name}`;
78001
+ console.log(line);
78002
+ }
78003
+ console.log("");
78004
+ }
78005
+ console.log("\u2500".repeat(60));
78006
+ console.log(
78007
+ `Summary: ${results.summary.ok}/${results.summary.total} checks passed`
78008
+ );
78009
+ if (results.summary.warn > 0) {
78010
+ console.log(`\u26A0 ${results.summary.warn} warnings`);
78011
+ }
78012
+ if (results.summary.fail > 0) {
78013
+ console.log(`\u2717 ${results.summary.fail} failures`);
78014
+ }
78015
+ console.log(`
78016
+ Overall status: ${results.status.toUpperCase()}`);
78017
+ console.log("");
78018
+ }
78019
+ function registerDoctorCommand(program3) {
78020
+ program3.command("doctor").description(
78021
+ "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"
78022
+ ).option("--json", "Output as JSON").action(async (options, command) => {
78023
+ const ctx = await createContext({
78024
+ format: options.json ? "json" : "text",
78025
+ verbose: command.optsWithGlobals().verbose,
78026
+ quiet: command.optsWithGlobals().quiet
78027
+ });
78028
+ const results = runHealthChecks();
78029
+ if (options.json || ctx.format === "json") {
78030
+ ctx.output.data(results);
78031
+ } else {
78032
+ formatTextOutput(results);
78033
+ }
78034
+ });
78035
+ }
78036
+
77885
78037
  // src/commands/eval.ts
77886
78038
  init_esm_shims();
77887
78039
  import { access, readFile as readFile2 } from "fs/promises";
@@ -79889,12 +80041,12 @@ async function scoreProduction(ctx, options) {
79889
80041
 
79890
80042
  // src/commands/eval-local/seed.ts
79891
80043
  init_esm_shims();
79892
- import { join as join4 } from "path";
80044
+ import { join as join5 } from "path";
79893
80045
  import { glob as glob3 } from "glob";
79894
80046
 
79895
80047
  // src/lib/eval-seed.ts
79896
80048
  init_esm_shims();
79897
- import { join as join3 } from "path";
80049
+ import { join as join4 } from "path";
79898
80050
  import { readFile as readFile5, readdir } from "fs/promises";
79899
80051
  import { glob as glob2 } from "glob";
79900
80052
  import matter from "gray-matter";
@@ -79940,7 +80092,7 @@ async function loadJsonFiles(dirPath) {
79940
80092
  const jsonFiles = files.filter((f) => f.endsWith(".json"));
79941
80093
  const items = await Promise.all(
79942
80094
  jsonFiles.map(async (file) => {
79943
- const content = await readFile5(join3(dirPath, file), "utf-8");
80095
+ const content = await readFile5(join4(dirPath, file), "utf-8");
79944
80096
  return JSON.parse(content);
79945
80097
  })
79946
80098
  );
@@ -79950,7 +80102,7 @@ async function loadJsonFiles(dirPath) {
79950
80102
  }
79951
80103
  }
79952
80104
  async function loadKnowledgeFiles(basePath) {
79953
- const files = await glob2(join3(basePath, "**/*.md"));
80105
+ const files = await glob2(join4(basePath, "**/*.md"));
79954
80106
  const docs = [];
79955
80107
  for (const filePath of files) {
79956
80108
  const content = await readFile5(filePath, "utf-8");
@@ -80140,16 +80292,16 @@ async function seed(ctx, options) {
80140
80292
  await cleanQdrant();
80141
80293
  }
80142
80294
  log("\u{1F4E6} Seeding apps...");
80143
- const apps = await loadJsonFiles(join4(fixturesPath, "apps"));
80295
+ const apps = await loadJsonFiles(join5(fixturesPath, "apps"));
80144
80296
  result.apps = await seedApps(connection, apps);
80145
80297
  log("\u{1F465} Loading customer fixtures...");
80146
- const customers = await loadJsonFiles(join4(fixturesPath, "customers"));
80298
+ const customers = await loadJsonFiles(join5(fixturesPath, "customers"));
80147
80299
  result.customers = customers.length;
80148
80300
  log("\u{1F4DA} Seeding knowledge base...");
80149
- const knowledge = await loadKnowledgeFiles(join4(fixturesPath, "knowledge"));
80301
+ const knowledge = await loadKnowledgeFiles(join5(fixturesPath, "knowledge"));
80150
80302
  result.knowledge = knowledge.length;
80151
80303
  result.embeddings = await seedKnowledgeBase(knowledge, !outputJson);
80152
- const scenarioFiles = await glob3(join4(fixturesPath, "scenarios/**/*.json"));
80304
+ const scenarioFiles = await glob3(join5(fixturesPath, "scenarios/**/*.json"));
80153
80305
  result.scenarios = scenarioFiles.length;
80154
80306
  await connection.end();
80155
80307
  if (outputJson) {
@@ -80225,8 +80377,8 @@ init_esm_shims();
80225
80377
  // src/commands/eval-pipeline/run.ts
80226
80378
  init_esm_shims();
80227
80379
  import { createHash as createHash2 } from "crypto";
80228
- import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync4 } from "fs";
80229
- import { join as join5 } from "path";
80380
+ import { existsSync as existsSync8, mkdirSync, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync4 } from "fs";
80381
+ import { join as join6 } from "path";
80230
80382
  import { readFile as readFile6 } from "fs/promises";
80231
80383
  import { glob as glob4 } from "glob";
80232
80384
 
@@ -80639,11 +80791,11 @@ function getCacheKey(scenarioId, classifySourceHash) {
80639
80791
  function getClassifySourceHash() {
80640
80792
  try {
80641
80793
  const possiblePaths = [
80642
- join5(process.cwd(), "packages/core/src/pipeline/classify.ts"),
80643
- join5(process.cwd(), "../core/src/pipeline/classify.ts")
80794
+ join6(process.cwd(), "packages/core/src/pipeline/classify.ts"),
80795
+ join6(process.cwd(), "../core/src/pipeline/classify.ts")
80644
80796
  ];
80645
80797
  for (const path of possiblePaths) {
80646
- if (existsSync6(path)) {
80798
+ if (existsSync8(path)) {
80647
80799
  const content = readFileSync5(path, "utf-8");
80648
80800
  return createHash2("md5").update(content).digest("hex");
80649
80801
  }
@@ -80653,9 +80805,9 @@ function getClassifySourceHash() {
80653
80805
  return createHash2("md5").update(Math.floor(Date.now() / 3e5).toString()).digest("hex");
80654
80806
  }
80655
80807
  function loadCachedClassify(cacheKey) {
80656
- const cachePath = join5(CACHE_DIR, `${cacheKey}.json`);
80808
+ const cachePath = join6(CACHE_DIR, `${cacheKey}.json`);
80657
80809
  try {
80658
- if (existsSync6(cachePath)) {
80810
+ if (existsSync8(cachePath)) {
80659
80811
  return JSON.parse(readFileSync5(cachePath, "utf-8"));
80660
80812
  }
80661
80813
  } catch {
@@ -80664,17 +80816,17 @@ function loadCachedClassify(cacheKey) {
80664
80816
  }
80665
80817
  function saveCachedClassify(cacheKey, result) {
80666
80818
  try {
80667
- if (!existsSync6(CACHE_DIR)) {
80819
+ if (!existsSync8(CACHE_DIR)) {
80668
80820
  mkdirSync(CACHE_DIR, { recursive: true });
80669
80821
  }
80670
- const cachePath = join5(CACHE_DIR, `${cacheKey}.json`);
80822
+ const cachePath = join6(CACHE_DIR, `${cacheKey}.json`);
80671
80823
  writeFileSync4(cachePath, JSON.stringify(result));
80672
80824
  } catch {
80673
80825
  }
80674
80826
  }
80675
80827
  function clearClassifyCache() {
80676
80828
  try {
80677
- if (existsSync6(CACHE_DIR)) {
80829
+ if (existsSync8(CACHE_DIR)) {
80678
80830
  rmSync(CACHE_DIR, { recursive: true, force: true });
80679
80831
  }
80680
80832
  } catch {
@@ -81346,7 +81498,7 @@ async function runValidateEval(ctx, scenarios, options) {
81346
81498
  return results;
81347
81499
  }
81348
81500
  async function runE2EEval(ctx, scenarios, options) {
81349
- const { runPipeline: runPipeline2 } = await import("./pipeline-TMFQSA7X.js");
81501
+ const { runPipeline: runPipeline2 } = await import("./pipeline-JPI7ITZN.js");
81350
81502
  const concurrency = options.parallel || 1;
81351
81503
  let completed = 0;
81352
81504
  const outputJson = options.outputJson ?? false;
@@ -81550,7 +81702,7 @@ Latency: ${avgLatency.toFixed(0)}ms avg`);
81550
81702
 
81551
81703
  // src/commands/eval-pipeline/seed.ts
81552
81704
  init_esm_shims();
81553
- import { join as join6 } from "path";
81705
+ import { join as join7 } from "path";
81554
81706
  import { glob as glob5 } from "glob";
81555
81707
  async function seed2(ctx, options) {
81556
81708
  const fixturesPath = options.fixtures || "fixtures";
@@ -81581,20 +81733,20 @@ async function seed2(ctx, options) {
81581
81733
  await cleanQdrant();
81582
81734
  }
81583
81735
  if (!outputJson) ctx.output.message("\u{1F4E6} Seeding apps...");
81584
- const apps = await loadJsonFiles(join6(fixturesPath, "apps"));
81736
+ const apps = await loadJsonFiles(join7(fixturesPath, "apps"));
81585
81737
  result.apps = await seedApps(connection, apps);
81586
81738
  const [trustRows] = await connection.execute(
81587
81739
  "SELECT COUNT(*) as count FROM SUPPORT_trust_scores"
81588
81740
  );
81589
81741
  result.trustScores = trustRows[0].count;
81590
81742
  if (!outputJson) ctx.output.message("\u{1F465} Loading customer fixtures...");
81591
- const customers = await loadJsonFiles(join6(fixturesPath, "customers"));
81743
+ const customers = await loadJsonFiles(join7(fixturesPath, "customers"));
81592
81744
  result.customers = customers.length;
81593
81745
  if (!outputJson) ctx.output.message("\u{1F4DA} Seeding knowledge base...");
81594
- const knowledge = await loadKnowledgeFiles(join6(fixturesPath, "knowledge"));
81746
+ const knowledge = await loadKnowledgeFiles(join7(fixturesPath, "knowledge"));
81595
81747
  result.knowledge = knowledge.length;
81596
81748
  result.embeddings = await seedKnowledgeBase(knowledge, !outputJson);
81597
- const scenarioFiles = await glob5(join6(fixturesPath, "scenarios/**/*.json"));
81749
+ const scenarioFiles = await glob5(join7(fixturesPath, "scenarios/**/*.json"));
81598
81750
  result.scenarios = scenarioFiles.length;
81599
81751
  await connection.end();
81600
81752
  if (outputJson) {
@@ -81666,7 +81818,7 @@ function registerEvalPipelineCommands(program3) {
81666
81818
 
81667
81819
  // src/commands/eval-prompt.ts
81668
81820
  init_esm_shims();
81669
- import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
81821
+ import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
81670
81822
  import { generateText as generateText2, stepCountIs as stepCountIs2, tool as tool4 } from "ai";
81671
81823
  import { z as z5 } from "zod";
81672
81824
  var leakPatterns = [
@@ -81853,7 +82005,7 @@ async function runEval2(ctx, options) {
81853
82005
  try {
81854
82006
  let prompt = SUPPORT_AGENT_PROMPT;
81855
82007
  if (promptPath) {
81856
- if (!existsSync7(promptPath)) {
82008
+ if (!existsSync9(promptPath)) {
81857
82009
  throw new CLIError({
81858
82010
  userMessage: `Prompt file not found: ${promptPath}.`,
81859
82011
  suggestion: "Verify the prompt path and try again."
@@ -81866,7 +82018,7 @@ async function runEval2(ctx, options) {
81866
82018
  } else if (!outputJson) {
81867
82019
  ctx.output.message("Using production prompt");
81868
82020
  }
81869
- if (!existsSync7(datasetPath)) {
82021
+ if (!existsSync9(datasetPath)) {
81870
82022
  throw new CLIError({
81871
82023
  userMessage: `Dataset not found: ${datasetPath}.`,
81872
82024
  suggestion: "Provide a valid dataset file path."
@@ -81978,7 +82130,7 @@ async function comparePrompts(ctx, options) {
81978
82130
  try {
81979
82131
  const baselinePrompt = baseline ? readFileSync6(baseline, "utf-8") : SUPPORT_AGENT_PROMPT;
81980
82132
  const candidatePrompt = readFileSync6(candidate, "utf-8");
81981
- if (!existsSync7(datasetPath)) {
82133
+ if (!existsSync9(datasetPath)) {
81982
82134
  throw new CLIError({
81983
82135
  userMessage: `Dataset not found: ${datasetPath}.`,
81984
82136
  suggestion: "Provide a valid dataset file path."
@@ -82126,20 +82278,20 @@ init_esm_shims();
82126
82278
 
82127
82279
  // src/commands/faq/classify.ts
82128
82280
  init_esm_shims();
82129
- import { appendFileSync, existsSync as existsSync8, mkdirSync as mkdirSync2, readFileSync as readFileSync7 } from "fs";
82130
- import { dirname as dirname2, join as join7, resolve as resolve3 } from "path";
82281
+ import { appendFileSync, existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as readFileSync7 } from "fs";
82282
+ import { dirname as dirname2, join as join8, resolve as resolve3 } from "path";
82131
82283
  import { generateObject } from "ai";
82132
82284
  import { z as z6 } from "zod";
82133
82285
  var PROJECT_ROOT = resolve3(__dirname, "../../../..");
82134
- var DEFAULT_PARQUET_PATH = join7(
82286
+ var DEFAULT_PARQUET_PATH = join8(
82135
82287
  PROJECT_ROOT,
82136
82288
  "artifacts/phase-0/embeddings/v2/conversations.parquet"
82137
82289
  );
82138
- var DEFAULT_TAXONOMY_PATH = join7(
82290
+ var DEFAULT_TAXONOMY_PATH = join8(
82139
82291
  PROJECT_ROOT,
82140
82292
  "artifacts/phase-1/llm-topics/taxonomy.json"
82141
82293
  );
82142
- var DEFAULT_OUTPUT_PATH = join7(
82294
+ var DEFAULT_OUTPUT_PATH = join8(
82143
82295
  PROJECT_ROOT,
82144
82296
  "artifacts/phase-1/llm-topics/classifications.jsonl"
82145
82297
  );
@@ -82173,7 +82325,7 @@ function createProgressReporter(ctx, total) {
82173
82325
  };
82174
82326
  }
82175
82327
  async function loadConversationsFromParquet(parquetPath) {
82176
- const { execSync: execSync3 } = await import("child_process");
82328
+ const { execSync: execSync4 } = await import("child_process");
82177
82329
  const query = `
82178
82330
  SELECT
82179
82331
  conversation_id,
@@ -82184,7 +82336,7 @@ async function loadConversationsFromParquet(parquetPath) {
82184
82336
  WHERE first_message IS NOT NULL
82185
82337
  ORDER BY conversation_id
82186
82338
  `;
82187
- const result = execSync3(`duckdb -json -c "${query.replace(/"/g, '\\"')}"`, {
82339
+ const result = execSync4(`duckdb -json -c "${query.replace(/"/g, '\\"')}"`, {
82188
82340
  encoding: "utf-8",
82189
82341
  maxBuffer: 100 * 1024 * 1024
82190
82342
  // 100MB buffer for large datasets
@@ -82194,7 +82346,7 @@ async function loadConversationsFromParquet(parquetPath) {
82194
82346
  }
82195
82347
  function loadExistingClassifications(outputPath) {
82196
82348
  const classifiedIds = /* @__PURE__ */ new Set();
82197
- if (!existsSync8(outputPath)) {
82349
+ if (!existsSync10(outputPath)) {
82198
82350
  return classifiedIds;
82199
82351
  }
82200
82352
  const content = readFileSync7(outputPath, "utf-8");
@@ -82312,7 +82464,7 @@ async function faqClassify(ctx, options) {
82312
82464
  ctx.output.data(` Dry run: ${options.dryRun ?? false}`);
82313
82465
  ctx.output.data("");
82314
82466
  }
82315
- if (!existsSync8(parquetPath)) {
82467
+ if (!existsSync10(parquetPath)) {
82316
82468
  handleFaqClassifyError(
82317
82469
  ctx,
82318
82470
  new CLIError({
@@ -82323,7 +82475,7 @@ async function faqClassify(ctx, options) {
82323
82475
  );
82324
82476
  return;
82325
82477
  }
82326
- if (!existsSync8(taxonomyPath)) {
82478
+ if (!existsSync10(taxonomyPath)) {
82327
82479
  handleFaqClassifyError(
82328
82480
  ctx,
82329
82481
  new CLIError({
@@ -82335,7 +82487,7 @@ async function faqClassify(ctx, options) {
82335
82487
  return;
82336
82488
  }
82337
82489
  const outputDir = dirname2(outputPath);
82338
- if (!existsSync8(outputDir)) {
82490
+ if (!existsSync10(outputDir)) {
82339
82491
  mkdirSync2(outputDir, { recursive: true });
82340
82492
  }
82341
82493
  if (!outputJson) ctx.output.data("\u{1F4DA} Loading taxonomy...");
@@ -82504,18 +82656,18 @@ function registerFaqClassifyCommands(program3) {
82504
82656
 
82505
82657
  // src/commands/faq/cluster.ts
82506
82658
  init_esm_shims();
82507
- import { existsSync as existsSync10 } from "fs";
82508
- import { join as join9, resolve as resolve4 } from "path";
82659
+ import { existsSync as existsSync12 } from "fs";
82660
+ import { join as join10, resolve as resolve4 } from "path";
82509
82661
 
82510
82662
  // ../core/src/faq/production-clusterer.ts
82511
82663
  init_esm_shims();
82512
- import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
82513
- import { join as join8 } from "path";
82664
+ import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
82665
+ import { join as join9 } from "path";
82514
82666
  function readPhase0Assignments(phase0Path) {
82515
- const assignmentsPath = join8(phase0Path, "clusters/v1/assignments.json");
82516
- if (!existsSync9(assignmentsPath)) {
82517
- const latestPath = join8(phase0Path, "clusters/latest/assignments.json");
82518
- if (!existsSync9(latestPath)) {
82667
+ const assignmentsPath = join9(phase0Path, "clusters/v1/assignments.json");
82668
+ if (!existsSync11(assignmentsPath)) {
82669
+ const latestPath = join9(phase0Path, "clusters/latest/assignments.json");
82670
+ if (!existsSync11(latestPath)) {
82519
82671
  throw new Error(`Phase 0 assignments not found at ${assignmentsPath}`);
82520
82672
  }
82521
82673
  const content2 = readFileSync8(latestPath, "utf-8");
@@ -82525,10 +82677,10 @@ function readPhase0Assignments(phase0Path) {
82525
82677
  return JSON.parse(content);
82526
82678
  }
82527
82679
  function readPhase0Labels(phase0Path) {
82528
- const labelsPath = join8(phase0Path, "clusters/v1/labels.json");
82529
- if (!existsSync9(labelsPath)) {
82530
- const latestPath = join8(phase0Path, "clusters/latest/labels.json");
82531
- if (!existsSync9(latestPath)) {
82680
+ const labelsPath = join9(phase0Path, "clusters/v1/labels.json");
82681
+ if (!existsSync11(labelsPath)) {
82682
+ const latestPath = join9(phase0Path, "clusters/latest/labels.json");
82683
+ if (!existsSync11(latestPath)) {
82532
82684
  throw new Error(`Phase 0 labels not found at ${labelsPath}`);
82533
82685
  }
82534
82686
  const content2 = readFileSync8(latestPath, "utf-8");
@@ -82540,10 +82692,10 @@ function readPhase0Labels(phase0Path) {
82540
82692
  return parsed.clusters || [];
82541
82693
  }
82542
82694
  function readPhase0Metrics(phase0Path) {
82543
- const metricsPath = join8(phase0Path, "clusters/v1/metrics.json");
82544
- if (!existsSync9(metricsPath)) {
82545
- const latestPath = join8(phase0Path, "clusters/latest/metrics.json");
82546
- if (!existsSync9(latestPath)) {
82695
+ const metricsPath = join9(phase0Path, "clusters/v1/metrics.json");
82696
+ if (!existsSync11(metricsPath)) {
82697
+ const latestPath = join9(phase0Path, "clusters/latest/metrics.json");
82698
+ if (!existsSync11(latestPath)) {
82547
82699
  throw new Error(`Phase 0 metrics not found at ${metricsPath}`);
82548
82700
  }
82549
82701
  return JSON.parse(readFileSync8(latestPath, "utf-8"));
@@ -82663,17 +82815,17 @@ async function generateProductionClustering(options) {
82663
82815
  return result;
82664
82816
  }
82665
82817
  function writeProductionArtifacts(result, outputPath) {
82666
- const versionPath = join8(outputPath, result.version);
82667
- if (!existsSync9(versionPath)) {
82818
+ const versionPath = join9(outputPath, result.version);
82819
+ if (!existsSync11(versionPath)) {
82668
82820
  mkdirSync3(versionPath, { recursive: true });
82669
82821
  }
82670
- const resultPath = join8(versionPath, "clustering-result.json");
82822
+ const resultPath = join9(versionPath, "clustering-result.json");
82671
82823
  writeFileSync6(resultPath, JSON.stringify(result, null, 2));
82672
82824
  console.log(`\u2705 Written: ${resultPath}`);
82673
- const assignmentsPath = join8(versionPath, "assignments.json");
82825
+ const assignmentsPath = join9(versionPath, "assignments.json");
82674
82826
  writeFileSync6(assignmentsPath, JSON.stringify(result.assignments, null, 2));
82675
82827
  console.log(`\u2705 Written: ${assignmentsPath}`);
82676
- const clustersPath = join8(versionPath, "clusters.json");
82828
+ const clustersPath = join9(versionPath, "clusters.json");
82677
82829
  writeFileSync6(
82678
82830
  clustersPath,
82679
82831
  JSON.stringify(
@@ -82688,7 +82840,7 @@ function writeProductionArtifacts(result, outputPath) {
82688
82840
  )
82689
82841
  );
82690
82842
  console.log(`\u2705 Written: ${clustersPath}`);
82691
- const summaryPath = join8(versionPath, "summary.json");
82843
+ const summaryPath = join9(versionPath, "summary.json");
82692
82844
  const summary = {
82693
82845
  version: result.version,
82694
82846
  generatedAt: result.generatedAt,
@@ -82702,8 +82854,8 @@ function writeProductionArtifacts(result, outputPath) {
82702
82854
  };
82703
82855
  writeFileSync6(summaryPath, JSON.stringify(summary, null, 2));
82704
82856
  console.log(`\u2705 Written: ${summaryPath}`);
82705
- const latestPath = join8(outputPath, "latest");
82706
- if (existsSync9(latestPath)) {
82857
+ const latestPath = join9(outputPath, "latest");
82858
+ if (existsSync11(latestPath)) {
82707
82859
  const { rmSync: rmSync2 } = __require("fs");
82708
82860
  rmSync2(latestPath, { recursive: true, force: true });
82709
82861
  }
@@ -82714,8 +82866,8 @@ function writeProductionArtifacts(result, outputPath) {
82714
82866
  "clusters.json",
82715
82867
  "summary.json"
82716
82868
  ]) {
82717
- const src = join8(versionPath, file);
82718
- const dst = join8(latestPath, file);
82869
+ const src = join9(versionPath, file);
82870
+ const dst = join9(latestPath, file);
82719
82871
  writeFileSync6(dst, readFileSync8(src));
82720
82872
  }
82721
82873
  console.log(`\u2705 Updated: ${latestPath}`);
@@ -82768,25 +82920,25 @@ function displayClusteringSummary(result) {
82768
82920
 
82769
82921
  // src/commands/faq/cluster.ts
82770
82922
  var PROJECT_ROOT2 = resolve4(__dirname, "../../../..");
82771
- var DEFAULT_PHASE0_PATH = join9(PROJECT_ROOT2, "artifacts/phase-0");
82772
- var DEFAULT_OUTPUT_PATH2 = join9(PROJECT_ROOT2, "artifacts/phase-1/clustering");
82923
+ var DEFAULT_PHASE0_PATH = join10(PROJECT_ROOT2, "artifacts/phase-0");
82924
+ var DEFAULT_OUTPUT_PATH2 = join10(PROJECT_ROOT2, "artifacts/phase-1/clustering");
82773
82925
  function validatePaths(phase0Path) {
82774
- const assignmentsPath = join9(phase0Path, "clusters/v1/assignments.json");
82775
- const labelsPath = join9(phase0Path, "clusters/v1/labels.json");
82776
- const metricsPath = join9(phase0Path, "clusters/v1/metrics.json");
82777
- if (!existsSync10(assignmentsPath)) {
82926
+ const assignmentsPath = join10(phase0Path, "clusters/v1/assignments.json");
82927
+ const labelsPath = join10(phase0Path, "clusters/v1/labels.json");
82928
+ const metricsPath = join10(phase0Path, "clusters/v1/metrics.json");
82929
+ if (!existsSync12(assignmentsPath)) {
82778
82930
  throw new CLIError({
82779
82931
  userMessage: `Phase 0 assignments not found at ${assignmentsPath}.`,
82780
82932
  suggestion: "Run Phase 0 clustering first or specify the correct --phase0-path."
82781
82933
  });
82782
82934
  }
82783
- if (!existsSync10(labelsPath)) {
82935
+ if (!existsSync12(labelsPath)) {
82784
82936
  throw new CLIError({
82785
82937
  userMessage: `Phase 0 labels not found at ${labelsPath}.`,
82786
82938
  suggestion: "Verify the --phase0-path points to valid artifacts."
82787
82939
  });
82788
82940
  }
82789
- if (!existsSync10(metricsPath)) {
82941
+ if (!existsSync12(metricsPath)) {
82790
82942
  throw new CLIError({
82791
82943
  userMessage: `Phase 0 metrics not found at ${metricsPath}.`,
82792
82944
  suggestion: "Verify the --phase0-path points to valid artifacts."
@@ -82824,7 +82976,7 @@ async function faqCluster(ctx, options) {
82824
82976
  writeProductionArtifacts(result, outputPath);
82825
82977
  if (!outputJson) {
82826
82978
  ctx.output.data("\n\u2705 Production clustering complete!");
82827
- ctx.output.data(` Artifacts written to: ${join9(outputPath, version)}`);
82979
+ ctx.output.data(` Artifacts written to: ${join10(outputPath, version)}`);
82828
82980
  }
82829
82981
  } else {
82830
82982
  if (!outputJson) ctx.output.data("\n\u{1F9EA} Dry run - no artifacts written");
@@ -82867,13 +83019,13 @@ function registerFaqClusterCommands(program3) {
82867
83019
 
82868
83020
  // src/commands/faq/extract.ts
82869
83021
  init_esm_shims();
82870
- import { existsSync as existsSync12 } from "fs";
82871
- import { join as join11, resolve as resolve5 } from "path";
83022
+ import { existsSync as existsSync14 } from "fs";
83023
+ import { join as join12, resolve as resolve5 } from "path";
82872
83024
 
82873
83025
  // ../core/src/faq/extractor.ts
82874
83026
  init_esm_shims();
82875
- import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
82876
- import { join as join10 } from "path";
83027
+ import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
83028
+ import { join as join11 } from "path";
82877
83029
 
82878
83030
  // ../core/src/faq/review.ts
82879
83031
  init_esm_shims();
@@ -83487,14 +83639,14 @@ async function extractFaqCandidates(options) {
83487
83639
  return result;
83488
83640
  }
83489
83641
  function writeExtractionArtifacts(result, outputPath) {
83490
- const versionPath = join10(outputPath, result.version);
83491
- if (!existsSync11(versionPath)) {
83642
+ const versionPath = join11(outputPath, result.version);
83643
+ if (!existsSync13(versionPath)) {
83492
83644
  mkdirSync4(versionPath, { recursive: true });
83493
83645
  }
83494
- const resultPath = join10(versionPath, "extraction-result.json");
83646
+ const resultPath = join11(versionPath, "extraction-result.json");
83495
83647
  writeFileSync7(resultPath, JSON.stringify(result, null, 2));
83496
83648
  console.log(`\u2705 Written: ${resultPath}`);
83497
- const candidatesPath = join10(versionPath, "candidates.json");
83649
+ const candidatesPath = join11(versionPath, "candidates.json");
83498
83650
  const candidatesData = {
83499
83651
  version: result.version,
83500
83652
  extractedAt: result.extractedAt,
@@ -83514,7 +83666,7 @@ function writeExtractionArtifacts(result, outputPath) {
83514
83666
  };
83515
83667
  writeFileSync7(candidatesPath, JSON.stringify(candidatesData, null, 2));
83516
83668
  console.log(`\u2705 Written: ${candidatesPath}`);
83517
- const statsPath = join10(versionPath, "stats.json");
83669
+ const statsPath = join11(versionPath, "stats.json");
83518
83670
  writeFileSync7(
83519
83671
  statsPath,
83520
83672
  JSON.stringify(
@@ -83528,8 +83680,8 @@ function writeExtractionArtifacts(result, outputPath) {
83528
83680
  )
83529
83681
  );
83530
83682
  console.log(`\u2705 Written: ${statsPath}`);
83531
- const latestPath = join10(outputPath, "latest");
83532
- if (existsSync11(latestPath)) {
83683
+ const latestPath = join11(outputPath, "latest");
83684
+ if (existsSync13(latestPath)) {
83533
83685
  const { rmSync: rmSync2 } = __require("fs");
83534
83686
  rmSync2(latestPath, { recursive: true, force: true });
83535
83687
  }
@@ -83539,9 +83691,9 @@ function writeExtractionArtifacts(result, outputPath) {
83539
83691
  "candidates.json",
83540
83692
  "stats.json"
83541
83693
  ]) {
83542
- const src = join10(versionPath, file);
83543
- const dst = join10(latestPath, file);
83544
- if (existsSync11(src)) {
83694
+ const src = join11(versionPath, file);
83695
+ const dst = join11(latestPath, file);
83696
+ if (existsSync13(src)) {
83545
83697
  writeFileSync7(dst, readFileSync9(src));
83546
83698
  }
83547
83699
  }
@@ -83595,24 +83747,24 @@ ${i + 1}. [${confPct}%]${golden} ${candidate.suggestedCategory}`
83595
83747
 
83596
83748
  // src/commands/faq/extract.ts
83597
83749
  var PROJECT_ROOT3 = resolve5(__dirname, "../../../..");
83598
- var DEFAULT_CLUSTERING_PATH = join11(
83750
+ var DEFAULT_CLUSTERING_PATH = join12(
83599
83751
  PROJECT_ROOT3,
83600
83752
  "artifacts/phase-1/clustering/v1/clustering-result.json"
83601
83753
  );
83602
- var DEFAULT_GOLDEN_PATH = join11(
83754
+ var DEFAULT_GOLDEN_PATH = join12(
83603
83755
  PROJECT_ROOT3,
83604
83756
  "artifacts/phase-0/golden/latest/responses.json"
83605
83757
  );
83606
- var DEFAULT_OUTPUT_PATH3 = join11(PROJECT_ROOT3, "artifacts/phase-1/extraction");
83758
+ var DEFAULT_OUTPUT_PATH3 = join12(PROJECT_ROOT3, "artifacts/phase-1/extraction");
83607
83759
  var DEFAULT_CACHE_PATH = `${process.env.HOME}/skill/data/front-cache.db`;
83608
83760
  function validatePaths2(ctx, clusteringPath, goldenPath, outputJson) {
83609
- if (!existsSync12(clusteringPath)) {
83761
+ if (!existsSync14(clusteringPath)) {
83610
83762
  throw new CLIError({
83611
83763
  userMessage: `Clustering result not found at ${clusteringPath}.`,
83612
83764
  suggestion: "Run `bun src/index.ts faq cluster` first to generate clustering."
83613
83765
  });
83614
83766
  }
83615
- if (goldenPath && !existsSync12(goldenPath)) {
83767
+ if (goldenPath && !existsSync14(goldenPath)) {
83616
83768
  if (!outputJson) {
83617
83769
  ctx.output.warn(`Golden responses not found at ${goldenPath}`);
83618
83770
  ctx.output.warn("Golden matching will be disabled.");
@@ -83641,7 +83793,7 @@ async function faqExtract(ctx, options) {
83641
83793
  ctx.output.data("");
83642
83794
  }
83643
83795
  validatePaths2(ctx, clusteringPath, goldenPath, outputJson);
83644
- if (!existsSync12(cachePath)) {
83796
+ if (!existsSync14(cachePath)) {
83645
83797
  const cliError = new CLIError({
83646
83798
  userMessage: `DuckDB cache not found at ${cachePath}.`,
83647
83799
  suggestion: "Run `bun src/index.ts front-cache sync` first to populate cache."
@@ -83663,7 +83815,7 @@ async function faqExtract(ctx, options) {
83663
83815
  }
83664
83816
  const extractionOptions = {
83665
83817
  clusteringPath,
83666
- goldenPath: existsSync12(goldenPath) ? goldenPath : void 0,
83818
+ goldenPath: existsSync14(goldenPath) ? goldenPath : void 0,
83667
83819
  source,
83668
83820
  outputPath,
83669
83821
  version,
@@ -83708,7 +83860,7 @@ async function faqExtract(ctx, options) {
83708
83860
  if (!options.dryRun) {
83709
83861
  ctx.output.data(`
83710
83862
  \u2705 Extraction complete!`);
83711
- ctx.output.data(` Artifacts written to: ${join11(outputPath, version)}`);
83863
+ ctx.output.data(` Artifacts written to: ${join12(outputPath, version)}`);
83712
83864
  if (options.pushRedis && options.app) {
83713
83865
  ctx.output.data(
83714
83866
  ` Candidates pushed to Redis queue: faq:pending:${options.app}`
@@ -92737,9 +92889,9 @@ function registerFaqMineCommands(program3) {
92737
92889
  // src/commands/faq/review.ts
92738
92890
  init_esm_shims();
92739
92891
  import { spawnSync } from "child_process";
92740
- import { existsSync as existsSync13, readFileSync as readFileSync10, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
92892
+ import { existsSync as existsSync15, readFileSync as readFileSync10, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
92741
92893
  import { tmpdir } from "os";
92742
- import { join as join12 } from "path";
92894
+ import { join as join13 } from "path";
92743
92895
  import { confirm as confirm2, select as select3 } from "@inquirer/prompts";
92744
92896
  var COLORS2 = {
92745
92897
  reset: "\x1B[0m",
@@ -92800,7 +92952,7 @@ function getEditor() {
92800
92952
  }
92801
92953
  function editInEditor(ctx, question, answer) {
92802
92954
  const editor = getEditor();
92803
- const tmpFile = join12(tmpdir(), `faq-edit-${Date.now()}.md`);
92955
+ const tmpFile = join13(tmpdir(), `faq-edit-${Date.now()}.md`);
92804
92956
  const content = `# FAQ Edit
92805
92957
 
92806
92958
  ## Question
@@ -92843,7 +92995,7 @@ The sections are separated by "## Question" and "## Answer" headers.
92843
92995
  answer: editedAnswer
92844
92996
  };
92845
92997
  } finally {
92846
- if (existsSync13(tmpFile)) {
92998
+ if (existsSync15(tmpFile)) {
92847
92999
  unlinkSync(tmpFile);
92848
93000
  }
92849
93001
  }
@@ -97795,6 +97947,154 @@ function registerInvestigateCommands(inngest) {
97795
97947
  });
97796
97948
  }
97797
97949
 
97950
+ // src/commands/inngest/patterns.ts
97951
+ init_esm_shims();
97952
+ async function patterns(ctx, options) {
97953
+ const outputJson = options.json === true || ctx.format === "json";
97954
+ try {
97955
+ const client = new InngestClient({ dev: options.dev });
97956
+ const params = { limit: 100 };
97957
+ if (options.after) {
97958
+ params.received_after = parseTimeArg(options.after);
97959
+ }
97960
+ const events = outputJson ? await client.listEvents(params) : await withSpinner("Fetching events...", () => client.listEvents(params));
97961
+ const byName = {};
97962
+ const byFunction = {};
97963
+ for (const event of events.data) {
97964
+ byName[event.name] = (byName[event.name] || 0) + 1;
97965
+ if (event.name === "inngest/function.finished") {
97966
+ const data2 = event.data;
97967
+ const functionId = data2?.function_id;
97968
+ if (!functionId) continue;
97969
+ if (!byFunction[functionId]) {
97970
+ byFunction[functionId] = { success: 0, failed: 0 };
97971
+ }
97972
+ if (data2.error || data2._inngest?.status === "Failed") {
97973
+ byFunction[functionId].failed++;
97974
+ } else {
97975
+ byFunction[functionId].success++;
97976
+ }
97977
+ }
97978
+ }
97979
+ const timeRangeHours = options.after ? parseTimeWindow(options.after) : 24;
97980
+ const topEvents = Object.entries(byName).map(([name, count]) => ({
97981
+ name,
97982
+ count,
97983
+ frequency_per_hour: count / timeRangeHours
97984
+ })).sort((a, b) => b.count - a.count).slice(0, 10);
97985
+ const functionStats = {};
97986
+ for (const [functionId, stats4] of Object.entries(byFunction)) {
97987
+ const total = stats4.success + stats4.failed;
97988
+ functionStats[functionId] = {
97989
+ success: stats4.success,
97990
+ failed: stats4.failed,
97991
+ success_rate: total > 0 ? stats4.success / total : 0
97992
+ };
97993
+ }
97994
+ const output = {
97995
+ time_range: options.after || "24h",
97996
+ total_events: events.data.length,
97997
+ events_by_name: byName,
97998
+ by_function: functionStats,
97999
+ top_events: topEvents
98000
+ };
98001
+ if (outputJson) {
98002
+ ctx.output.data(output);
98003
+ } else {
98004
+ const lines = [];
98005
+ lines.push("\nEvent Patterns\n");
98006
+ lines.push(`Time range: ${output.time_range}`);
98007
+ lines.push(`Total events: ${output.total_events}
98008
+ `);
98009
+ lines.push("Top Events:");
98010
+ lines.push(
98011
+ "\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"
98012
+ );
98013
+ lines.push(
98014
+ "\u2502 Event Name \u2502 Count \u2502 Per Hour \u2502"
98015
+ );
98016
+ lines.push(
98017
+ "\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"
98018
+ );
98019
+ for (const event of topEvents) {
98020
+ const name = event.name.padEnd(38).slice(0, 38);
98021
+ const count = String(event.count).padStart(5);
98022
+ const freq = event.frequency_per_hour.toFixed(2).padStart(12);
98023
+ lines.push(`\u2502 ${name} \u2502 ${count} \u2502 ${freq} \u2502`);
98024
+ }
98025
+ lines.push(
98026
+ "\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"
98027
+ );
98028
+ if (Object.keys(functionStats).length > 0) {
98029
+ lines.push("Function Stats:");
98030
+ lines.push(
98031
+ "\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"
98032
+ );
98033
+ lines.push(
98034
+ "\u2502 Function \u2502 Success \u2502 Failed \u2502 Success % \u2502"
98035
+ );
98036
+ lines.push(
98037
+ "\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"
98038
+ );
98039
+ for (const [functionId, stats4] of Object.entries(functionStats)) {
98040
+ const name = functionId.padEnd(38).slice(0, 38);
98041
+ const success = String(stats4.success).padStart(7);
98042
+ const failed = String(stats4.failed).padStart(6);
98043
+ const rate = `${(stats4.success_rate * 100).toFixed(1)}%`.padStart(11);
98044
+ lines.push(`\u2502 ${name} \u2502 ${success} \u2502 ${failed} \u2502 ${rate} \u2502`);
98045
+ }
98046
+ lines.push(
98047
+ "\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"
98048
+ );
98049
+ }
98050
+ lines.push(
98051
+ "Use `skill inngest failures` for detailed failure analysis.\n"
98052
+ );
98053
+ ctx.output.data(lines.join("\n"));
98054
+ }
98055
+ } catch (error) {
98056
+ const cliError = error instanceof CLIError ? error : new CLIError({
98057
+ userMessage: "Failed to analyze Inngest patterns.",
98058
+ suggestion: "Verify INNGEST_SIGNING_KEY and time window.",
98059
+ cause: error
98060
+ });
98061
+ ctx.output.error(formatError(cliError));
98062
+ process.exitCode = cliError.exitCode;
98063
+ }
98064
+ }
98065
+ function parseTimeWindow(input2) {
98066
+ const match = input2.match(/^(\d+)([hmd])$/);
98067
+ if (!match) return 24;
98068
+ const [, num, unit] = match;
98069
+ const value = Number.parseInt(num ?? "24", 10);
98070
+ switch (unit) {
98071
+ case "h":
98072
+ return value;
98073
+ case "d":
98074
+ return value * 24;
98075
+ case "m":
98076
+ return value / 60;
98077
+ default:
98078
+ return 24;
98079
+ }
98080
+ }
98081
+ function registerPatternsCommand(inngest) {
98082
+ inngest.command("patterns").description(
98083
+ "Aggregate event analysis (event distribution, function success rates)"
98084
+ ).option("--after <time>", 'Time window (e.g., "2h", "1d")', "24h").option("--json", "Output as JSON").option("--dev", "Use dev server").action(async (options, command) => {
98085
+ const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
98086
+ ...command.parent?.opts(),
98087
+ ...command.opts()
98088
+ };
98089
+ const ctx = await createContext({
98090
+ format: options.json ? "json" : opts.format,
98091
+ verbose: opts.verbose,
98092
+ quiet: opts.quiet
98093
+ });
98094
+ await patterns(ctx, options);
98095
+ });
98096
+ }
98097
+
97798
98098
  // src/commands/inngest/runs.ts
97799
98099
  init_esm_shims();
97800
98100
  import { confirm as confirm4 } from "@inquirer/prompts";
@@ -97984,11 +98284,16 @@ function registerSignalCommand(inngest) {
97984
98284
 
97985
98285
  // src/commands/inngest/index.ts
97986
98286
  function registerInngestCommands(program3, usageState2) {
97987
- const inngest = program3.command("inngest").description(getInngestAdaptiveDescription(usageState2));
98287
+ const baseDescription = getInngestAdaptiveDescription(usageState2);
98288
+ const descriptionWithEnv = `${baseDescription}
98289
+
98290
+ Environment: INNGEST_SIGNING_KEY required. Run \`skill doctor\` to check.`;
98291
+ const inngest = program3.command("inngest").description(descriptionWithEnv);
97988
98292
  registerEventsCommands(inngest);
97989
98293
  registerRunsCommands(inngest);
97990
98294
  registerSignalCommand(inngest);
97991
98295
  registerInvestigateCommands(inngest);
98296
+ registerPatternsCommand(inngest);
97992
98297
  }
97993
98298
 
97994
98299
  // src/commands/kb-sync.ts
@@ -114017,7 +114322,7 @@ function registerKbCommands(program3) {
114017
114322
 
114018
114323
  // src/commands/keys/index.ts
114019
114324
  init_esm_shims();
114020
- import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
114325
+ import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
114021
114326
  import { password as password2, select as select5 } from "@inquirer/prompts";
114022
114327
  import { Decrypter as Decrypter4 } from "age-encryption";
114023
114328
  var buildContext4 = async (command, json) => {
@@ -114032,9 +114337,9 @@ var buildContext4 = async (command, json) => {
114032
114337
  });
114033
114338
  };
114034
114339
  async function getUserConfiguredKeys() {
114035
- const keyPath = getAgeKeyPath();
114340
+ const keyPath = getAgeKeyPath2();
114036
114341
  const configPath = getEncryptedConfigPath();
114037
- if (!existsSync14(keyPath) || !existsSync14(configPath)) {
114342
+ if (!existsSync16(keyPath) || !existsSync16(configPath)) {
114038
114343
  return /* @__PURE__ */ new Set();
114039
114344
  }
114040
114345
  try {
@@ -114101,8 +114406,8 @@ async function showKeyStatus(ctx) {
114101
114406
  ctx.output.data("");
114102
114407
  }
114103
114408
  async function interactiveKeySetup(ctx) {
114104
- const keyPath = getAgeKeyPath();
114105
- if (!existsSync14(keyPath)) {
114409
+ const keyPath = getAgeKeyPath2();
114410
+ if (!existsSync16(keyPath)) {
114106
114411
  ctx.output.data("\n\u{1F511} First time setup - creating your encryption key...\n");
114107
114412
  await configInitAction(ctx, { json: false });
114108
114413
  ctx.output.data("");
@@ -114194,8 +114499,8 @@ function registerKeysCommands(program3) {
114194
114499
  "Add a personal API key\n\n Interactive: skill keys add\n Direct: skill keys add LINEAR_API_KEY=lin_xxx"
114195
114500
  ).option("--json", "Output as JSON").action(async (keyValue, options, command) => {
114196
114501
  const ctx = await buildContext4(command, options.json);
114197
- const keyPath = getAgeKeyPath();
114198
- if (!existsSync14(keyPath)) {
114502
+ const keyPath = getAgeKeyPath2();
114503
+ if (!existsSync16(keyPath)) {
114199
114504
  if (process.stdin.isTTY && !options.json) {
114200
114505
  ctx.output.data(
114201
114506
  "\u{1F511} First time setup - creating your encryption key...\n"
@@ -114244,8 +114549,8 @@ function registerKeysCommands(program3) {
114244
114549
  steps.push("\u2713 OP token from env");
114245
114550
  }
114246
114551
  if (!opToken) {
114247
- const { getFromKeychain } = await import("./keychain-IEZHT5WN.js");
114248
- opToken = getFromKeychain("op-service-account-token");
114552
+ const { getFromKeychain: getFromKeychain2 } = await import("./keychain-IEZHT5WN.js");
114553
+ opToken = getFromKeychain2("op-service-account-token");
114249
114554
  if (opToken) steps.push("\u2713 OP token from keychain");
114250
114555
  }
114251
114556
  if (!opToken && status.opCliAvailable) {
@@ -114265,8 +114570,8 @@ function registerKeysCommands(program3) {
114265
114570
  steps.push("\u2713 Age key from env");
114266
114571
  }
114267
114572
  if (!ageKey) {
114268
- const { getFromKeychain } = await import("./keychain-IEZHT5WN.js");
114269
- ageKey = getFromKeychain("age-private-key");
114573
+ const { getFromKeychain: getFromKeychain2 } = await import("./keychain-IEZHT5WN.js");
114574
+ ageKey = getFromKeychain2("age-private-key");
114270
114575
  if (ageKey) steps.push("\u2713 Age key from keychain");
114271
114576
  }
114272
114577
  if (!ageKey && opToken) {
@@ -115637,6 +115942,25 @@ var PRIORITY_EMOJI2 = {
115637
115942
  3: "\u{1F7E2}",
115638
115943
  4: "\u26AA"
115639
115944
  };
115945
+ function parseRelativeTime(timeStr) {
115946
+ const match = timeStr.match(/^(\d+)(d|w|h|m)$/);
115947
+ if (!match || !match[1] || !match[2]) {
115948
+ throw new CLIError({
115949
+ userMessage: `Invalid time format: ${timeStr}`,
115950
+ suggestion: "Use format like 90d, 2w, 24h, or 3m"
115951
+ });
115952
+ }
115953
+ const value = parseInt(match[1], 10);
115954
+ const unit = match[2];
115955
+ const msMap = {
115956
+ h: 60 * 60 * 1e3,
115957
+ d: 24 * 60 * 60 * 1e3,
115958
+ w: 7 * 24 * 60 * 60 * 1e3,
115959
+ m: 30 * 24 * 60 * 60 * 1e3
115960
+ };
115961
+ const ms = value * msMap[unit];
115962
+ return new Date(Date.now() - ms);
115963
+ }
115640
115964
  async function listIssues(ctx, options = {}) {
115641
115965
  const limit2 = options.limit || 20;
115642
115966
  try {
@@ -115648,6 +115972,10 @@ async function listIssues(ctx, options = {}) {
115648
115972
  }
115649
115973
  }
115650
115974
  };
115975
+ if (options.olderThan) {
115976
+ const date = parseRelativeTime(options.olderThan);
115977
+ filter4.updatedAt = { lt: date };
115978
+ }
115651
115979
  let teamKey;
115652
115980
  if (options.team) {
115653
115981
  const teams = await client.teams();
@@ -115709,28 +116037,57 @@ async function listIssues(ctx, options = {}) {
115709
116037
  });
115710
116038
  const issues = response.nodes || [];
115711
116039
  const issuesWithDetails = await Promise.all(
115712
- issues.map(async (issue) => ({
115713
- issue,
115714
- state: await issue.state,
115715
- assignee: await issue.assignee,
115716
- team: await issue.team
115717
- }))
116040
+ issues.map(async (issue) => {
116041
+ const labels = options.export ? await issue.labels() : null;
116042
+ const comments = options.export ? await issue.comments() : null;
116043
+ return {
116044
+ issue,
116045
+ state: await issue.state,
116046
+ assignee: await issue.assignee,
116047
+ team: await issue.team,
116048
+ labels: labels?.nodes || null,
116049
+ comments: comments?.nodes || null
116050
+ };
116051
+ })
115718
116052
  );
115719
116053
  if (ctx.format === "json") {
115720
- const issueData = issuesWithDetails.map(
115721
- ({ issue, state, assignee, team }) => ({
115722
- id: issue.id,
115723
- identifier: issue.identifier,
115724
- title: issue.title,
115725
- state: state?.name || null,
115726
- stateType: state?.type || null,
115727
- priority: issue.priority,
115728
- assignee: assignee ? { id: assignee.id, name: assignee.name, email: assignee.email } : null,
115729
- team: team ? { key: team.key, name: team.name } : null,
115730
- url: issue.url,
115731
- createdAt: issue.createdAt,
115732
- updatedAt: issue.updatedAt
115733
- })
116054
+ const issueData = await Promise.all(
116055
+ issuesWithDetails.map(
116056
+ async ({ issue, state, assignee, team, labels, comments }) => {
116057
+ const baseData = {
116058
+ id: issue.id,
116059
+ identifier: issue.identifier,
116060
+ title: issue.title,
116061
+ state: state?.name || null,
116062
+ stateType: state?.type || null,
116063
+ priority: issue.priority,
116064
+ assignee: assignee ? {
116065
+ id: assignee.id,
116066
+ name: assignee.name,
116067
+ email: assignee.email
116068
+ } : null,
116069
+ team: team ? { key: team.key, name: team.name } : null,
116070
+ url: issue.url,
116071
+ createdAt: issue.createdAt,
116072
+ updatedAt: issue.updatedAt
116073
+ };
116074
+ if (options.export) {
116075
+ return {
116076
+ ...baseData,
116077
+ description: issue.description,
116078
+ labels: labels ? labels.map((l) => l.name) : [],
116079
+ comments: comments ? await Promise.all(
116080
+ comments.map(async (c) => ({
116081
+ body: c.body,
116082
+ user: (await c.user)?.name || "Unknown",
116083
+ createdAt: c.createdAt
116084
+ }))
116085
+ ) : []
116086
+ };
116087
+ }
116088
+ return baseData;
116089
+ }
116090
+ )
115734
116091
  );
115735
116092
  ctx.output.data(
115736
116093
  JSON.stringify(
@@ -115778,7 +116135,14 @@ async function listIssues(ctx, options = {}) {
115778
116135
  ctx.output.data("");
115779
116136
  return;
115780
116137
  }
115781
- for (const { issue, state, assignee, team } of issuesWithDetails) {
116138
+ for (const {
116139
+ issue,
116140
+ state,
116141
+ assignee,
116142
+ team,
116143
+ labels,
116144
+ comments
116145
+ } of issuesWithDetails) {
115782
116146
  const emoji = PRIORITY_EMOJI2[issue.priority] || "\u26AA";
115783
116147
  const assigneeName = assignee ? `@${assignee.name}` : "";
115784
116148
  const teamBadge = team ? `[${team.key}]` : "";
@@ -115789,6 +116153,19 @@ async function listIssues(ctx, options = {}) {
115789
116153
  ctx.output.data(
115790
116154
  ` Status: ${state?.name || "unknown"}${assigneeName ? ` | Assignee: ${assigneeName}` : ""}`
115791
116155
  );
116156
+ if (options.export) {
116157
+ if (issue.description) {
116158
+ ctx.output.data(` Description: ${issue.description}`);
116159
+ }
116160
+ if (labels && labels.length > 0) {
116161
+ ctx.output.data(
116162
+ ` Labels: ${labels.map((l) => l.name).join(", ")}`
116163
+ );
116164
+ }
116165
+ if (comments && comments.length > 0) {
116166
+ ctx.output.data(` Comments: ${comments.length}`);
116167
+ }
116168
+ }
115792
116169
  }
115793
116170
  ctx.output.data("");
115794
116171
  ctx.output.data(" Use `skill linear issue <ID> --json` for full details.");
@@ -116582,6 +116959,13 @@ Quick start:
116582
116959
  skill linear create "Title" Create issue
116583
116960
  skill linear search "query" Search issues
116584
116961
 
116962
+ Bulk queries:
116963
+ skill linear issues --older-than 90d Stale issues
116964
+ skill linear issues --older-than 2w --export Full data export
116965
+
116966
+ Environment:
116967
+ LINEAR_API_KEY required. Run 'skill doctor' to check.
116968
+
116585
116969
  All commands support --json for machine-readable output.
116586
116970
 
116587
116971
  \u26A0\uFE0F Write operations require personal LINEAR_API_KEY (run 'skill keys add').`
@@ -116594,8 +116978,10 @@ Examples:
116594
116978
  skill linear issues --team ENG Filter by team
116595
116979
  skill linear issues --state "In Progress" Filter by state
116596
116980
  skill linear issues --assignee me Your issues
116597
- skill linear issues --priority 0 Urgent only`
116598
- ).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) => {
116981
+ skill linear issues --priority 0 Urgent only
116982
+ skill linear issues --older-than 90d Stale issues (90+ days)
116983
+ skill linear issues --older-than 2w --export Full export`
116984
+ ).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) => {
116599
116985
  const ctx = await contextFromCommand2(command, options);
116600
116986
  await listIssues(ctx, {
116601
116987
  limit: parseInt(options.limit || "20", 10),
@@ -116603,7 +116989,9 @@ Examples:
116603
116989
  state: options.state,
116604
116990
  assignee: options.assignee,
116605
116991
  project: options.project,
116606
- priority: options.priority !== void 0 ? parseInt(options.priority, 10) : void 0
116992
+ priority: options.priority !== void 0 ? parseInt(options.priority, 10) : void 0,
116993
+ olderThan: options.olderThan,
116994
+ export: options.export
116607
116995
  });
116608
116996
  });
116609
116997
  linear.command("my").description(
@@ -116850,6 +117238,101 @@ Examples:
116850
117238
  });
116851
117239
  }
116852
117240
 
117241
+ // src/commands/list.ts
117242
+ init_esm_shims();
117243
+ import { existsSync as existsSync17, readFileSync as readFileSync13, readdirSync, statSync } from "fs";
117244
+ import { join as join14 } from "path";
117245
+ function discoverSkills(skillsDir) {
117246
+ if (!existsSync17(skillsDir)) {
117247
+ return [];
117248
+ }
117249
+ const skills = [];
117250
+ try {
117251
+ const entries = readdirSync(skillsDir);
117252
+ for (const entry of entries) {
117253
+ const entryPath = join14(skillsDir, entry);
117254
+ const stat = statSync(entryPath);
117255
+ if (!stat.isDirectory()) continue;
117256
+ const skillPath = join14(entryPath, "SKILL.md");
117257
+ if (!existsSync17(skillPath)) continue;
117258
+ const content = readFileSync13(skillPath, "utf8");
117259
+ const description = extractDescription(content);
117260
+ skills.push({
117261
+ name: entry,
117262
+ description,
117263
+ path: skillPath
117264
+ });
117265
+ }
117266
+ } catch {
117267
+ return [];
117268
+ }
117269
+ return skills.sort((a, b) => a.name.localeCompare(b.name));
117270
+ }
117271
+ function extractDescription(markdown) {
117272
+ const lines = markdown.split("\n");
117273
+ let inFrontmatter = false;
117274
+ let foundHeading = false;
117275
+ const descriptionLines = [];
117276
+ for (const line of lines) {
117277
+ const trimmed = line.trim();
117278
+ if (trimmed === "---") {
117279
+ if (!inFrontmatter && !foundHeading) {
117280
+ inFrontmatter = true;
117281
+ continue;
117282
+ } else if (inFrontmatter) {
117283
+ inFrontmatter = false;
117284
+ continue;
117285
+ }
117286
+ }
117287
+ if (inFrontmatter) continue;
117288
+ if (trimmed.startsWith("# ")) {
117289
+ foundHeading = true;
117290
+ continue;
117291
+ }
117292
+ if (foundHeading && trimmed.startsWith("#")) {
117293
+ break;
117294
+ }
117295
+ if (foundHeading) {
117296
+ if (trimmed === "") {
117297
+ if (descriptionLines.length > 0) {
117298
+ break;
117299
+ }
117300
+ continue;
117301
+ }
117302
+ descriptionLines.push(trimmed);
117303
+ }
117304
+ }
117305
+ return descriptionLines.join(" ").trim();
117306
+ }
117307
+ async function listAction(options, command) {
117308
+ const ctx = await createContext({
117309
+ format: options.json ? "json" : command.optsWithGlobals().format,
117310
+ verbose: command.optsWithGlobals().verbose,
117311
+ quiet: command.optsWithGlobals().quiet
117312
+ });
117313
+ const skillsDir = join14(process.cwd(), ".claude", "skills");
117314
+ const skills = discoverSkills(skillsDir);
117315
+ if (options.json) {
117316
+ ctx.output.data({ skills });
117317
+ return;
117318
+ }
117319
+ if (skills.length === 0) {
117320
+ ctx.output.message("No skills found in .claude/skills/");
117321
+ return;
117322
+ }
117323
+ ctx.output.data(`
117324
+ \u{1F4DA} Available Skills (${skills.length})`);
117325
+ ctx.output.data("\u2500".repeat(80));
117326
+ const rows = skills.map((skill) => ({
117327
+ Name: skill.name,
117328
+ Description: skill.description || "(no description)"
117329
+ }));
117330
+ ctx.output.table(rows);
117331
+ }
117332
+ function registerListCommand(program3) {
117333
+ program3.command("list").description("List available CLI skills for programmatic discovery").option("--json", "Output as JSON").action(listAction);
117334
+ }
117335
+
116853
117336
  // src/commands/memory/index.ts
116854
117337
  init_esm_shims();
116855
117338
 
@@ -117426,7 +117909,7 @@ async function runE2EEval2(options) {
117426
117909
  verbose,
117427
117910
  json,
117428
117911
  limit: limit2,
117429
- model = "anthropic/claude-haiku-4-5"
117912
+ model = "anthropic/claude-sonnet-4-5"
117430
117913
  } = options;
117431
117914
  const content = await readFile8(dataset, "utf-8");
117432
117915
  let scenarios = JSON.parse(content);
@@ -117868,7 +118351,7 @@ var handlePipelineError = (ctx, error, message, suggestion = "Verify inputs and
117868
118351
  async function runPipelineCommand(ctx, opts) {
117869
118352
  const outputJson = opts.json === true || ctx.format === "json";
117870
118353
  try {
117871
- const { runPipeline: runPipeline2 } = await import("./pipeline-TMFQSA7X.js");
118354
+ const { runPipeline: runPipeline2 } = await import("./pipeline-JPI7ITZN.js");
117872
118355
  const result = await runPipeline2({
117873
118356
  message: {
117874
118357
  subject: opts.subject,
@@ -117979,16 +118462,16 @@ function registerPipelineCommands(program3) {
117979
118462
  // src/commands/plugin-sync.ts
117980
118463
  init_esm_shims();
117981
118464
  import { homedir as homedir3 } from "os";
117982
- import { dirname as dirname3, join as join13, resolve as resolve6 } from "path";
118465
+ import { dirname as dirname3, join as join15, resolve as resolve6 } from "path";
117983
118466
  import { fileURLToPath } from "url";
117984
118467
  var PLUGIN_SOURCE_DIR = resolve6(
117985
118468
  dirname3(fileURLToPath(import.meta.url)),
117986
118469
  "../../plugin"
117987
118470
  );
117988
- var PLUGIN_MANIFEST_RELATIVE = join13(".claude-plugin", "plugin.json");
118471
+ var PLUGIN_MANIFEST_RELATIVE = join15(".claude-plugin", "plugin.json");
117989
118472
  var resolveTargetDir = (global2) => {
117990
- const base = join13(homedir3(), ".claude", global2 ? "skills" : "plugins");
117991
- return join13(base, "skill-cli");
118473
+ const base = join15(homedir3(), ".claude", global2 ? "skills" : "plugins");
118474
+ return join15(base, "skill-cli");
117992
118475
  };
117993
118476
  var readManifest = async (path) => {
117994
118477
  const manifest = await readJson(path);
@@ -118017,7 +118500,7 @@ var writeResult4 = (ctx, payload) => {
118017
118500
  };
118018
118501
  async function executePluginSync(ctx, options) {
118019
118502
  try {
118020
- const sourceManifestPath = join13(PLUGIN_SOURCE_DIR, PLUGIN_MANIFEST_RELATIVE);
118503
+ const sourceManifestPath = join15(PLUGIN_SOURCE_DIR, PLUGIN_MANIFEST_RELATIVE);
118021
118504
  const sourceExists = await pathExists(sourceManifestPath);
118022
118505
  if (!sourceExists) {
118023
118506
  throw new CLIError({
@@ -118027,7 +118510,7 @@ async function executePluginSync(ctx, options) {
118027
118510
  }
118028
118511
  const sourceManifest = await readManifest(sourceManifestPath);
118029
118512
  const targetDir = resolveTargetDir(options.global);
118030
- const targetManifestPath = join13(targetDir, PLUGIN_MANIFEST_RELATIVE);
118513
+ const targetManifestPath = join15(targetDir, PLUGIN_MANIFEST_RELATIVE);
118031
118514
  const targetExists = await pathExists(targetManifestPath);
118032
118515
  if (targetExists && !options.force) {
118033
118516
  const targetManifest = await readManifest(targetManifestPath);
@@ -119270,7 +119753,7 @@ init_esm_shims();
119270
119753
  import { spawn } from "child_process";
119271
119754
  import { writeFile as writeFile7 } from "fs/promises";
119272
119755
  import { homedir as homedir4 } from "os";
119273
- import { dirname as dirname4, join as join14 } from "path";
119756
+ import { dirname as dirname4, join as join16 } from "path";
119274
119757
  var CONFIG_DIR_NAME = "skill-cli";
119275
119758
  var AUTO_UPDATE_STATE_FILE = "auto-update.json";
119276
119759
  var DEFAULT_PACKAGE = "@skillrecordings/cli";
@@ -119282,7 +119765,7 @@ var AutoUpdateStore = class {
119282
119765
  now;
119283
119766
  constructor(options = {}) {
119284
119767
  const configDir = resolveConfigDir(options.configDir);
119285
- this.filePath = join14(configDir, AUTO_UPDATE_STATE_FILE);
119768
+ this.filePath = join16(configDir, AUTO_UPDATE_STATE_FILE);
119286
119769
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
119287
119770
  }
119288
119771
  getNow() {
@@ -119310,9 +119793,9 @@ function resolveConfigDir(configDir) {
119310
119793
  if (configDir) return configDir;
119311
119794
  const xdgConfigHome = process.env.XDG_CONFIG_HOME;
119312
119795
  if (xdgConfigHome && xdgConfigHome.trim() !== "") {
119313
- return join14(xdgConfigHome, CONFIG_DIR_NAME);
119796
+ return join16(xdgConfigHome, CONFIG_DIR_NAME);
119314
119797
  }
119315
- return join14(homedir4(), ".config", CONFIG_DIR_NAME);
119798
+ return join16(homedir4(), ".config", CONFIG_DIR_NAME);
119316
119799
  }
119317
119800
  function isAutoUpdateState(value) {
119318
119801
  if (!value || typeof value !== "object") return false;
@@ -119609,13 +120092,13 @@ var writeHints = (hints, stderr) => {
119609
120092
  init_esm_shims();
119610
120093
  import { lstat, readlink, symlink } from "fs/promises";
119611
120094
  import { homedir as homedir5 } from "os";
119612
- import { dirname as dirname5, join as join15, resolve as resolve7 } from "path";
120095
+ import { dirname as dirname5, join as join17, resolve as resolve7 } from "path";
119613
120096
  import { fileURLToPath as fileURLToPath2 } from "url";
119614
120097
  var SKILL_SOURCE_DIR = resolve7(
119615
120098
  dirname5(fileURLToPath2(import.meta.url)),
119616
120099
  "../../../../.claude/skills/skill-cli"
119617
120100
  );
119618
- var SKILL_TARGET_DIR = join15(homedir5(), ".claude", "skills", "skill-cli");
120101
+ var SKILL_TARGET_DIR = join17(homedir5(), ".claude", "skills", "skill-cli");
119619
120102
  async function autoLinkSkill() {
119620
120103
  const source = SKILL_SOURCE_DIR;
119621
120104
  const target = SKILL_TARGET_DIR;
@@ -119719,16 +120202,16 @@ async function sendTelemetryEvent(event) {
119719
120202
  init_esm_shims();
119720
120203
  import { writeFile as writeFile8 } from "fs/promises";
119721
120204
  import { homedir as homedir6 } from "os";
119722
- import { dirname as dirname6, join as join16 } from "path";
120205
+ import { dirname as dirname6, join as join18 } from "path";
119723
120206
  var CONFIG_DIR_NAME2 = "skill-cli";
119724
120207
  var USAGE_FILE_NAME = "usage.json";
119725
120208
  function resolveConfigDir2(configDir) {
119726
120209
  if (configDir) return configDir;
119727
120210
  const xdgConfigHome = process.env.XDG_CONFIG_HOME;
119728
120211
  if (xdgConfigHome && xdgConfigHome.trim() !== "") {
119729
- return join16(xdgConfigHome, CONFIG_DIR_NAME2);
120212
+ return join18(xdgConfigHome, CONFIG_DIR_NAME2);
119730
120213
  }
119731
- return join16(homedir6(), ".config", CONFIG_DIR_NAME2);
120214
+ return join18(homedir6(), ".config", CONFIG_DIR_NAME2);
119732
120215
  }
119733
120216
  function createDefaultState(now) {
119734
120217
  return {
@@ -119766,7 +120249,7 @@ var UsageTracker = class {
119766
120249
  statePromise;
119767
120250
  constructor(options = {}) {
119768
120251
  const configDir = resolveConfigDir2(options.configDir);
119769
- this.filePath = join16(configDir, USAGE_FILE_NAME);
120252
+ this.filePath = join18(configDir, USAGE_FILE_NAME);
119770
120253
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
119771
120254
  }
119772
120255
  async loadState() {
@@ -120345,8 +120828,8 @@ if (!envLoaded && !process.env.DATABASE_URL) {
120345
120828
  process.env.SKIP_ENV_VALIDATION = "1";
120346
120829
  }
120347
120830
  var runtimeTarget = `bun-${process.platform}-${process.arch}`;
120348
- var buildVersion = "0.17.0".length > 0 ? "0.17.0" : "0.0.0-dev";
120349
- var buildCommit = "e664abd".length > 0 ? "e664abd" : "dev";
120831
+ var buildVersion = "0.18.1".length > 0 ? "0.18.1" : "0.0.0-dev";
120832
+ var buildCommit = "e9e5214".length > 0 ? "e9e5214" : "dev";
120350
120833
  var buildTarget = "node".length > 0 ? "node" : runtimeTarget;
120351
120834
  var isDevBuild = buildVersion.includes("dev") || buildCommit === "dev";
120352
120835
  var versionLabel = `skill v${buildVersion} (${buildCommit}) ${buildTarget}`;
@@ -120551,6 +121034,8 @@ registerKbCommands(program2);
120551
121034
  registerAuthCommands(program2, usageState);
120552
121035
  registerConfigCommands(program2);
120553
121036
  registerKeysCommands(program2);
121037
+ registerDoctorCommand(program2);
121038
+ registerListCommand(program2);
120554
121039
  registerPluginSyncCommand(program2);
120555
121040
  program2.command("mcp").description(
120556
121041
  "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)"