@skillrecordings/cli 0.15.0 → 0.16.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
@@ -1479,9 +1479,9 @@ var require_url_state_machine = __commonJS({
1479
1479
  url.username += percentEncodeChar(decoded[i], isUserinfoPercentEncode);
1480
1480
  }
1481
1481
  };
1482
- module.exports.setThePassword = function(url, password2) {
1482
+ module.exports.setThePassword = function(url, password3) {
1483
1483
  url.password = "";
1484
- const decoded = punycode.ucs2.decode(password2);
1484
+ const decoded = punycode.ucs2.decode(password3);
1485
1485
  for (let i = 0; i < decoded.length; ++i) {
1486
1486
  url.password += percentEncodeChar(decoded[i], isUserinfoPercentEncode);
1487
1487
  }
@@ -16736,7 +16736,7 @@ var require_proxy_agent = __commonJS({
16736
16736
  const { proxyTunnel = true } = opts;
16737
16737
  super();
16738
16738
  const url = this.#getUrl(opts);
16739
- const { href, origin, port, protocol, username, password: password2, hostname: proxyHostname } = url;
16739
+ const { href, origin, port, protocol, username, password: password3, hostname: proxyHostname } = url;
16740
16740
  this[kProxy] = { uri: href, protocol };
16741
16741
  this[kRequestTls] = opts.requestTls;
16742
16742
  this[kProxyTls] = opts.proxyTls;
@@ -16748,8 +16748,8 @@ var require_proxy_agent = __commonJS({
16748
16748
  this[kProxyHeaders]["proxy-authorization"] = `Basic ${opts.auth}`;
16749
16749
  } else if (opts.token) {
16750
16750
  this[kProxyHeaders]["proxy-authorization"] = opts.token;
16751
- } else if (username && password2) {
16752
- this[kProxyHeaders]["proxy-authorization"] = `Basic ${Buffer.from(`${decodeURIComponent(username)}:${decodeURIComponent(password2)}`).toString("base64")}`;
16751
+ } else if (username && password3) {
16752
+ this[kProxyHeaders]["proxy-authorization"] = `Basic ${Buffer.from(`${decodeURIComponent(username)}:${decodeURIComponent(password3)}`).toString("base64")}`;
16753
16753
  }
16754
16754
  const connect = buildConnector({ ...opts.proxyTls });
16755
16755
  this[kConnectEndpoint] = buildConnector({ ...opts.requestTls });
@@ -19843,7 +19843,7 @@ var require_snapshot_recorder = __commonJS({
19843
19843
  "use strict";
19844
19844
  init_esm_shims();
19845
19845
  var { writeFile: writeFile9, readFile: readFile10, mkdir: mkdir2 } = __require("fs/promises");
19846
- var { dirname: dirname8, resolve: resolve9 } = __require("path");
19846
+ var { dirname: dirname7, resolve: resolve9 } = __require("path");
19847
19847
  var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("timers");
19848
19848
  var { InvalidArgumentError, UndiciError } = require_errors();
19849
19849
  var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
@@ -20074,7 +20074,7 @@ var require_snapshot_recorder = __commonJS({
20074
20074
  throw new InvalidArgumentError("Snapshot path is required");
20075
20075
  }
20076
20076
  const resolvedPath = resolve9(path);
20077
- await mkdir2(dirname8(resolvedPath), { recursive: true });
20077
+ await mkdir2(dirname7(resolvedPath), { recursive: true });
20078
20078
  const data2 = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
20079
20079
  hash,
20080
20080
  snapshot
@@ -26406,8 +26406,8 @@ var require_fetch = __commonJS({
26406
26406
  let authorizationValue = null;
26407
26407
  if (hasAuthenticationEntry(httpRequest) && (httpRequest.useURLCredentials === void 0 || !includesCredentials(requestCurrentURL(httpRequest)))) {
26408
26408
  } else if (includesCredentials(requestCurrentURL(httpRequest)) && isAuthenticationFetch) {
26409
- const { username, password: password2 } = requestCurrentURL(httpRequest);
26410
- authorizationValue = `Basic ${Buffer.from(`${username}:${password2}`).toString("base64")}`;
26409
+ const { username, password: password3 } = requestCurrentURL(httpRequest);
26410
+ authorizationValue = `Basic ${Buffer.from(`${username}:${password3}`).toString("base64")}`;
26411
26411
  }
26412
26412
  if (authorizationValue !== null) {
26413
26413
  httpRequest.headersList.append("Authorization", authorizationValue, false);
@@ -74714,24 +74714,24 @@ init_esm_shims();
74714
74714
  var ABBREVIATED_THRESHOLD = 2;
74715
74715
  var MINIMAL_THRESHOLD = 5;
74716
74716
  var ROOT_DESCRIPTIONS = {
74717
- full: "Skill Recordings support agent CLI \u2014 triage, investigate, and manage customer conversations.\n\n Getting Started:\n 1. skill auth setup Configure 1Password secrets (Front API token, DB, etc.)\n 2. skill auth status Verify your credentials are working\n 3. skill front inbox See what needs attention right now\n\n Common Workflows:\n Triage inbox skill front inbox \u2192 skill front triage\n Investigate ticket skill front conversation <id> --messages\n Bulk cleanup skill front bulk-archive --older-than 30d\n Generate report skill front report --inbox support\n Check deploys skill deploys\n\n For AI Agents (Claude Code, MCP):\n skill mcp Start JSON-RPC server with 9 Front tools\n skill plugin sync Install the Claude Code plugin\n All commands support --json for structured, HATEOAS-enriched output",
74718
- abbreviated: "Skill Recordings support agent CLI \u2014 triage and investigate support conversations.\n\n Start here:\n skill auth setup Configure 1Password + secrets\n skill front inbox See what needs attention\n skill front triage Auto-categorize conversations\n\n Common:\n skill front conversation <id> --messages\n skill front reply <id>\n skill deploys\n skill mcp\n",
74719
- minimal: "Skill Recordings support agent CLI. Try: skill front inbox, skill front triage, skill auth status. Use --help for details."
74717
+ full: "Skill Recordings support agent CLI \u2014 triage, investigate, and manage customer conversations.\n\n Getting Started:\n 1. skill wizard Interactive app setup wizard\n 2. skill keys Manage your personal API keys\n 3. skill front inbox See what needs attention right now\n\n Common Workflows:\n Triage inbox skill front inbox \u2192 skill front triage\n Investigate ticket skill front conversation <id> --messages\n Bulk cleanup skill front bulk-archive --older-than 30d\n Generate report skill front report --inbox support\n Check deploys skill deploys\n\n For AI Agents (Claude Code, MCP):\n skill mcp Start JSON-RPC server with 9 Front tools\n skill plugin sync Install the Claude Code plugin\n All commands support --json for structured, HATEOAS-enriched output",
74718
+ abbreviated: "Skill Recordings support agent CLI \u2014 triage and investigate support conversations.\n\n Start here:\n skill wizard Set up a new product\n skill keys Manage API keys\n skill front inbox See what needs attention\n skill front triage Auto-categorize conversations\n\n Common:\n skill front conversation <id> --messages\n skill front reply <id>\n skill deploys\n skill mcp\n",
74719
+ minimal: "Skill Recordings support agent CLI. Try: skill wizard, skill keys, skill front inbox, skill front triage. Use --help for details."
74720
74720
  };
74721
74721
  var FRONT_DESCRIPTIONS = {
74722
- full: "Front conversations, inboxes, tags, archival, and reporting.\n\n Prerequisites:\n FRONT_API_TOKEN must be set. Run: skill auth setup\n\n Start here:\n skill front inbox See unassigned conversations\n skill front inbox support List conversations in a specific inbox\n skill front triage AI-powered categorization of inbox items\n\n Investigate a conversation:\n skill front conversation <id> -m Full conversation with messages\n skill front message <id> Single message details + body\n\n Take action:\n skill front assign <id> Assign to a teammate\n skill front reply <id> Draft a reply (HITL, never auto-sends)\n skill front tag <id> Add a tag\n skill front archive <id> Archive a resolved conversation\n\n Bulk operations:\n skill front bulk-archive Archive old/spam conversations\n skill front report Volume + tag + sender forensics\n\n All commands accept --json for HATEOAS-enriched output with _links and _actions.",
74723
- abbreviated: "Front API workflows for inbox triage and conversation actions.\n\n Common:\n skill front inbox\n skill front triage\n skill front conversation <id> -m\n skill front reply <id>\n skill front archive <id>\n\n Requires FRONT_API_TOKEN (skill auth setup).",
74724
- minimal: "Front API commands (inbox, triage, assign, reply, archive). Requires FRONT_API_TOKEN."
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.",
74723
+ abbreviated: "Front API workflows for inbox triage and conversation actions.\n\n Common:\n skill front inbox\n skill front triage\n skill front conversation <id> -m\n skill front reply <id>\n skill front archive <id>",
74724
+ minimal: "Front API commands (inbox, triage, assign, reply, archive)."
74725
74725
  };
74726
74726
  var AUTH_DESCRIPTIONS = {
74727
- full: "Manage CLI authentication and secrets.\n\n First time? Start here:\n skill auth setup Interactive wizard \u2014 connects to 1Password vault,\n configures FRONT_API_TOKEN, DATABASE_URL, and more.\n Requires: `op` CLI installed (brew install 1password-cli)\n\n Check your setup:\n skill auth status Shows which secrets are configured and which are missing\n skill auth whoami Verify your 1Password service account identity\n\n All Skill Recordings employees have access to the Support vault in 1Password.\n The setup wizard handles everything \u2014 no manual token pasting needed.",
74728
- abbreviated: "Manage CLI authentication and 1Password secrets.\n\n Start:\n skill auth setup\n skill auth status\n skill auth whoami\n\n Requires the 1Password CLI (`op`).",
74729
- minimal: "Auth and 1Password setup commands."
74727
+ full: "View CLI authentication status.\n\n Check your setup:\n skill auth status Shows which secrets are loaded\n skill auth whoami Verify your 1Password service account identity\n\n API keys ship with the CLI (encrypted). To override with personal keys:\n skill keys Manage personal API key overrides",
74728
+ abbreviated: "View CLI authentication status.\n\n Commands:\n skill auth status\n skill auth whoami",
74729
+ minimal: "Auth status commands (auth status, auth whoami)."
74730
74730
  };
74731
74731
  var INNGEST_DESCRIPTIONS = {
74732
- full: "Inngest event and workflow commands.\n\n Debug pipeline runs:\n skill inngest runs --status failed --after 1h Recent failures\n skill inngest events --after 12h Recent events\n skill inngest investigate <run-id> Deep-dive a specific run\n\n Requires: INNGEST_SIGNING_KEY, INNGEST_EVENT_KEY in env",
74733
- abbreviated: "Inngest events and workflow runs.\n\n Common:\n skill inngest runs --status failed --after 1h\n skill inngest events --after 12h\n skill inngest investigate <run-id>\n\n Requires INNGEST_SIGNING_KEY, INNGEST_EVENT_KEY.",
74734
- minimal: "Inngest events and runs debugging. Requires INNGEST_SIGNING_KEY and INNGEST_EVENT_KEY."
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",
74733
+ abbreviated: "Inngest events and workflow runs.\n\n Common:\n skill inngest runs --status failed --after 1h\n skill inngest events --after 12h\n skill inngest investigate <run-id>",
74734
+ minimal: "Inngest events and runs debugging."
74735
74735
  };
74736
74736
  var GROUP_DESCRIPTIONS = {
74737
74737
  root: ROOT_DESCRIPTIONS,
@@ -76954,11 +76954,11 @@ Saved to ${options.output}`);
76954
76954
  }
76955
76955
  }
76956
76956
  async function toEvalite(options) {
76957
- const { readFileSync: readFileSync12 } = await import("fs");
76957
+ const { readFileSync: readFileSync13 } = await import("fs");
76958
76958
  const { ctx } = options;
76959
76959
  const outputJson = ctx.format === "json";
76960
76960
  const data2 = JSON.parse(
76961
- readFileSync12(options.input, "utf-8")
76961
+ readFileSync13(options.input, "utf-8")
76962
76962
  );
76963
76963
  const evaliteData = data2.map((d) => ({
76964
76964
  input: d.triggerMessage.body,
@@ -77019,15 +77019,19 @@ init_esm_shims();
77019
77019
 
77020
77020
  // src/commands/config/get.ts
77021
77021
  init_esm_shims();
77022
- import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
77022
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
77023
77023
  import { Decrypter as Decrypter2 } from "age-encryption";
77024
77024
 
77025
+ // src/commands/config/set.ts
77026
+ init_esm_shims();
77027
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
77028
+ import { password, select as select2 } from "@inquirer/prompts";
77029
+ import { Decrypter, Encrypter, identityToRecipient } from "age-encryption";
77030
+
77025
77031
  // src/commands/config/init.ts
77026
77032
  init_esm_shims();
77027
- import { existsSync as existsSync3, mkdirSync, writeFileSync as writeFileSync3 } from "fs";
77028
77033
  import { homedir as homedir2 } from "os";
77029
77034
  import { join as join2 } from "path";
77030
- import { generateIdentity, identityToRecipient } from "age-encryption";
77031
77035
  function getUserConfigDir2() {
77032
77036
  return join2(homedir2(), ".config", "skill");
77033
77037
  }
@@ -77036,68 +77040,27 @@ function getAgeKeyPath() {
77036
77040
  }
77037
77041
  async function configInitAction(ctx, options = {}) {
77038
77042
  const outputJson = options.json === true || ctx.format === "json";
77039
- const keyPath = getAgeKeyPath();
77040
- const configDir = getUserConfigDir2();
77041
- if (existsSync3(keyPath) && !options.force) {
77042
- const result = {
77043
- success: false,
77044
- error: `Age key already exists at ${keyPath}. Use --force to overwrite.`
77045
- };
77046
- if (outputJson) {
77047
- ctx.output.data(result);
77048
- } else {
77049
- ctx.output.error(result.error);
77050
- ctx.output.data(`
77051
- To view your public key: skill config public-key`);
77052
- }
77053
- process.exitCode = EXIT_CODES.usage;
77054
- return;
77055
- }
77056
- try {
77057
- if (!existsSync3(configDir)) {
77058
- mkdirSync(configDir, { recursive: true, mode: 448 });
77059
- }
77060
- const identity = await generateIdentity();
77061
- const recipient = await identityToRecipient(identity);
77062
- writeFileSync3(keyPath, identity, { encoding: "utf8", mode: 384 });
77063
- const result = {
77064
- success: true,
77065
- keyPath,
77066
- publicKey: recipient
77067
- };
77068
- if (outputJson) {
77069
- ctx.output.data(result);
77070
- } else {
77071
- ctx.output.success("Age keypair generated successfully!");
77072
- ctx.output.data(`
77073
- Private key saved to: ${keyPath}`);
77074
- ctx.output.data(`Public key (age recipient): ${recipient}`);
77075
- ctx.output.data(
77076
- "\n\u26A0\uFE0F Keep your private key secure. Anyone with access can decrypt your config."
77077
- );
77078
- ctx.output.data("\nNext steps:");
77079
- ctx.output.data(" 1. Set config values: skill config set KEY=value");
77080
- ctx.output.data(" 2. View config: skill config list");
77081
- }
77082
- } catch (error) {
77083
- const result = {
77084
- success: false,
77085
- error: error instanceof Error ? error.message : "Failed to generate keypair"
77086
- };
77087
- if (outputJson) {
77088
- ctx.output.data(result);
77089
- } else {
77090
- ctx.output.error(`Failed to generate keypair: ${result.error}`);
77091
- }
77092
- process.exitCode = EXIT_CODES.error;
77043
+ const result = {
77044
+ success: false,
77045
+ error: "DEPRECATED: skill config init is no longer needed.\nThe CLI now uses the 1Password age key for encryption.\nUse `skill keys` to manage your personal API keys."
77046
+ };
77047
+ if (outputJson) {
77048
+ ctx.output.data(result);
77049
+ } else {
77050
+ ctx.output.error("\u26A0\uFE0F DEPRECATED: skill config init is no longer needed.");
77051
+ ctx.output.data("");
77052
+ ctx.output.data("The CLI now uses the 1Password age key for encryption.");
77053
+ ctx.output.data("Local age keypairs are no longer required.");
77054
+ ctx.output.data("");
77055
+ ctx.output.data("To manage your personal API keys:");
77056
+ ctx.output.data(" skill keys Interactive setup");
77057
+ ctx.output.data(" skill keys add Add a personal API key");
77058
+ ctx.output.data(" skill keys status Show key provenance");
77093
77059
  }
77060
+ process.exitCode = EXIT_CODES.usage;
77094
77061
  }
77095
77062
 
77096
77063
  // src/commands/config/set.ts
77097
- init_esm_shims();
77098
- import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync4 } from "fs";
77099
- import { password, select as select2 } from "@inquirer/prompts";
77100
- import { Decrypter, Encrypter, identityToRecipient as identityToRecipient2 } from "age-encryption";
77101
77064
  function getEncryptedConfigPath() {
77102
77065
  return `${getUserConfigDir2()}/.env.user.encrypted`;
77103
77066
  }
@@ -77110,7 +77073,7 @@ function parseKeyValue(input2) {
77110
77073
  return { key, value };
77111
77074
  }
77112
77075
  async function readExistingConfig(identity, configPath) {
77113
- if (!existsSync4(configPath)) {
77076
+ if (!existsSync3(configPath)) {
77114
77077
  return {};
77115
77078
  }
77116
77079
  try {
@@ -77137,7 +77100,7 @@ async function readExistingConfig(identity, configPath) {
77137
77100
  async function configSetAction(ctx, keyValue, options = {}) {
77138
77101
  const outputJson = options.json === true || ctx.format === "json";
77139
77102
  const keyPath = getAgeKeyPath();
77140
- if (!existsSync4(keyPath)) {
77103
+ if (!existsSync3(keyPath)) {
77141
77104
  const result = {
77142
77105
  success: false,
77143
77106
  error: "Age key not found. Run: skill config init"
@@ -77189,7 +77152,7 @@ async function configSetAction(ctx, keyValue, options = {}) {
77189
77152
  }
77190
77153
  try {
77191
77154
  const identity = readFileSync2(keyPath, "utf8").trim();
77192
- const recipient = await identityToRecipient2(identity);
77155
+ const recipient = await identityToRecipient(identity);
77193
77156
  const configPath = getEncryptedConfigPath();
77194
77157
  const config = await readExistingConfig(identity, configPath);
77195
77158
  config[parsed.key] = parsed.value;
@@ -77197,7 +77160,7 @@ async function configSetAction(ctx, keyValue, options = {}) {
77197
77160
  const encrypter = new Encrypter();
77198
77161
  encrypter.addRecipient(recipient);
77199
77162
  const encrypted = await encrypter.encrypt(envContent + "\n");
77200
- writeFileSync4(configPath, Buffer.from(encrypted));
77163
+ writeFileSync3(configPath, Buffer.from(encrypted));
77201
77164
  const result = {
77202
77165
  success: true,
77203
77166
  key: parsed.key,
@@ -77224,9 +77187,24 @@ async function configSetAction(ctx, keyValue, options = {}) {
77224
77187
  }
77225
77188
 
77226
77189
  // src/commands/config/get.ts
77190
+ async function getAgeKeyFrom1Password2() {
77191
+ if (!process.env.OP_SERVICE_ACCOUNT_TOKEN) {
77192
+ return null;
77193
+ }
77194
+ try {
77195
+ const { OnePasswordProvider: OnePasswordProvider2 } = await import("./secrets-MGVPGMFJ.js");
77196
+ const op = new OnePasswordProvider2();
77197
+ if (!await op.isAvailable()) {
77198
+ return null;
77199
+ }
77200
+ return await op.resolve("op://Support/skill-cli-age-key/private_key");
77201
+ } catch {
77202
+ return null;
77203
+ }
77204
+ }
77227
77205
  async function decryptConfig(identity) {
77228
77206
  const configPath = getEncryptedConfigPath();
77229
- if (!existsSync5(configPath)) {
77207
+ if (!existsSync4(configPath)) {
77230
77208
  return {};
77231
77209
  }
77232
77210
  const encrypted = readFileSync3(configPath);
@@ -77254,11 +77232,11 @@ async function decryptConfig(identity) {
77254
77232
  }
77255
77233
  async function configGetAction(ctx, key, options = {}) {
77256
77234
  const outputJson = options.json === true || ctx.format === "json";
77257
- const keyPath = getAgeKeyPath();
77258
- if (!existsSync5(keyPath)) {
77235
+ const identity = await getAgeKeyFrom1Password2();
77236
+ if (!identity) {
77259
77237
  const result = {
77260
77238
  success: false,
77261
- error: "Age key not found. Run: skill config init"
77239
+ error: "OP_SERVICE_ACCOUNT_TOKEN not set. Required for encrypted config."
77262
77240
  };
77263
77241
  if (outputJson) {
77264
77242
  ctx.output.data(result);
@@ -77269,7 +77247,6 @@ async function configGetAction(ctx, key, options = {}) {
77269
77247
  return;
77270
77248
  }
77271
77249
  try {
77272
- const identity = readFileSync3(keyPath, "utf8").trim();
77273
77250
  const config = await decryptConfig(identity);
77274
77251
  if (!(key in config)) {
77275
77252
  const result2 = {
@@ -77312,11 +77289,26 @@ async function configGetAction(ctx, key, options = {}) {
77312
77289
 
77313
77290
  // src/commands/config/list.ts
77314
77291
  init_esm_shims();
77315
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
77292
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
77316
77293
  import { Decrypter as Decrypter3 } from "age-encryption";
77294
+ async function getAgeKeyFrom1Password3() {
77295
+ if (!process.env.OP_SERVICE_ACCOUNT_TOKEN) {
77296
+ return null;
77297
+ }
77298
+ try {
77299
+ const { OnePasswordProvider: OnePasswordProvider2 } = await import("./secrets-MGVPGMFJ.js");
77300
+ const op = new OnePasswordProvider2();
77301
+ if (!await op.isAvailable()) {
77302
+ return null;
77303
+ }
77304
+ return await op.resolve("op://Support/skill-cli-age-key/private_key");
77305
+ } catch {
77306
+ return null;
77307
+ }
77308
+ }
77317
77309
  async function decryptConfig2(identity) {
77318
77310
  const configPath = getEncryptedConfigPath();
77319
- if (!existsSync6(configPath)) {
77311
+ if (!existsSync5(configPath)) {
77320
77312
  return {};
77321
77313
  }
77322
77314
  const encrypted = readFileSync4(configPath);
@@ -77344,11 +77336,11 @@ async function decryptConfig2(identity) {
77344
77336
  }
77345
77337
  async function configListAction(ctx, options = {}) {
77346
77338
  const outputJson = options.json === true || ctx.format === "json";
77347
- const keyPath = getAgeKeyPath();
77348
- if (!existsSync6(keyPath)) {
77339
+ const identity = await getAgeKeyFrom1Password3();
77340
+ if (!identity) {
77349
77341
  const result = {
77350
77342
  success: false,
77351
- error: "Age key not found. Run: skill config init"
77343
+ error: "OP_SERVICE_ACCOUNT_TOKEN not set. Required for encrypted config."
77352
77344
  };
77353
77345
  if (outputJson) {
77354
77346
  ctx.output.data(result);
@@ -77359,7 +77351,6 @@ async function configListAction(ctx, options = {}) {
77359
77351
  return;
77360
77352
  }
77361
77353
  try {
77362
- const identity = readFileSync4(keyPath, "utf8").trim();
77363
77354
  const config = await decryptConfig2(identity);
77364
77355
  const keys = Object.keys(config);
77365
77356
  if (keys.length === 0) {
@@ -77424,7 +77415,7 @@ var buildContext2 = async (command, json) => {
77424
77415
  };
77425
77416
  function registerConfigCommands(program3) {
77426
77417
  const config = program3.command("config").description("Manage user-specific config overrides (encrypted)");
77427
- config.command("init").description("Generate age keypair for user config encryption").option("--force", "Overwrite existing keypair").option("--json", "Output as JSON").action(async (options, command) => {
77418
+ config.command("init").description("[DEPRECATED] No longer needed - use `skill keys` instead").option("--force", "Overwrite existing keypair").option("--json", "Output as JSON").action(async (options, command) => {
77428
77419
  const ctx = await buildContext2(command, options.json);
77429
77420
  await configInitAction(ctx, options);
77430
77421
  });
@@ -80114,7 +80105,7 @@ init_esm_shims();
80114
80105
  // src/commands/eval-pipeline/run.ts
80115
80106
  init_esm_shims();
80116
80107
  import { createHash as createHash2 } from "crypto";
80117
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync5 } from "fs";
80108
+ import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync4 } from "fs";
80118
80109
  import { join as join5 } from "path";
80119
80110
  import { readFile as readFile6 } from "fs/promises";
80120
80111
  import { glob as glob4 } from "glob";
@@ -80532,7 +80523,7 @@ function getClassifySourceHash() {
80532
80523
  join5(process.cwd(), "../core/src/pipeline/classify.ts")
80533
80524
  ];
80534
80525
  for (const path of possiblePaths) {
80535
- if (existsSync7(path)) {
80526
+ if (existsSync6(path)) {
80536
80527
  const content = readFileSync5(path, "utf-8");
80537
80528
  return createHash2("md5").update(content).digest("hex");
80538
80529
  }
@@ -80544,7 +80535,7 @@ function getClassifySourceHash() {
80544
80535
  function loadCachedClassify(cacheKey) {
80545
80536
  const cachePath = join5(CACHE_DIR, `${cacheKey}.json`);
80546
80537
  try {
80547
- if (existsSync7(cachePath)) {
80538
+ if (existsSync6(cachePath)) {
80548
80539
  return JSON.parse(readFileSync5(cachePath, "utf-8"));
80549
80540
  }
80550
80541
  } catch {
@@ -80553,17 +80544,17 @@ function loadCachedClassify(cacheKey) {
80553
80544
  }
80554
80545
  function saveCachedClassify(cacheKey, result) {
80555
80546
  try {
80556
- if (!existsSync7(CACHE_DIR)) {
80557
- mkdirSync2(CACHE_DIR, { recursive: true });
80547
+ if (!existsSync6(CACHE_DIR)) {
80548
+ mkdirSync(CACHE_DIR, { recursive: true });
80558
80549
  }
80559
80550
  const cachePath = join5(CACHE_DIR, `${cacheKey}.json`);
80560
- writeFileSync5(cachePath, JSON.stringify(result));
80551
+ writeFileSync4(cachePath, JSON.stringify(result));
80561
80552
  } catch {
80562
80553
  }
80563
80554
  }
80564
80555
  function clearClassifyCache() {
80565
80556
  try {
80566
- if (existsSync7(CACHE_DIR)) {
80557
+ if (existsSync6(CACHE_DIR)) {
80567
80558
  rmSync(CACHE_DIR, { recursive: true, force: true });
80568
80559
  }
80569
80560
  } catch {
@@ -81555,7 +81546,7 @@ function registerEvalPipelineCommands(program3) {
81555
81546
 
81556
81547
  // src/commands/eval-prompt.ts
81557
81548
  init_esm_shims();
81558
- import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
81549
+ import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
81559
81550
  import { generateText as generateText2, stepCountIs as stepCountIs2, tool as tool4 } from "ai";
81560
81551
  import { z as z5 } from "zod";
81561
81552
  var leakPatterns = [
@@ -81742,7 +81733,7 @@ async function runEval2(ctx, options) {
81742
81733
  try {
81743
81734
  let prompt = SUPPORT_AGENT_PROMPT;
81744
81735
  if (promptPath) {
81745
- if (!existsSync8(promptPath)) {
81736
+ if (!existsSync7(promptPath)) {
81746
81737
  throw new CLIError({
81747
81738
  userMessage: `Prompt file not found: ${promptPath}.`,
81748
81739
  suggestion: "Verify the prompt path and try again."
@@ -81755,7 +81746,7 @@ async function runEval2(ctx, options) {
81755
81746
  } else if (!outputJson) {
81756
81747
  ctx.output.message("Using production prompt");
81757
81748
  }
81758
- if (!existsSync8(datasetPath)) {
81749
+ if (!existsSync7(datasetPath)) {
81759
81750
  throw new CLIError({
81760
81751
  userMessage: `Dataset not found: ${datasetPath}.`,
81761
81752
  suggestion: "Provide a valid dataset file path."
@@ -81838,7 +81829,7 @@ async function runEval2(ctx, options) {
81838
81829
  });
81839
81830
  }
81840
81831
  if (outputPath) {
81841
- writeFileSync6(outputPath, JSON.stringify(results, null, 2));
81832
+ writeFileSync5(outputPath, JSON.stringify(results, null, 2));
81842
81833
  if (!outputJson) {
81843
81834
  ctx.output.success(`Saved results to ${outputPath}`);
81844
81835
  }
@@ -81867,7 +81858,7 @@ async function comparePrompts(ctx, options) {
81867
81858
  try {
81868
81859
  const baselinePrompt = baseline ? readFileSync6(baseline, "utf-8") : SUPPORT_AGENT_PROMPT;
81869
81860
  const candidatePrompt = readFileSync6(candidate, "utf-8");
81870
- if (!existsSync8(datasetPath)) {
81861
+ if (!existsSync7(datasetPath)) {
81871
81862
  throw new CLIError({
81872
81863
  userMessage: `Dataset not found: ${datasetPath}.`,
81873
81864
  suggestion: "Provide a valid dataset file path."
@@ -82015,8 +82006,8 @@ init_esm_shims();
82015
82006
 
82016
82007
  // src/commands/faq/classify.ts
82017
82008
  init_esm_shims();
82018
- import { appendFileSync, existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync7 } from "fs";
82019
- import { dirname as dirname3, join as join7, resolve as resolve3 } from "path";
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";
82020
82011
  import { generateObject } from "ai";
82021
82012
  import { z as z6 } from "zod";
82022
82013
  var PROJECT_ROOT = resolve3(__dirname, "../../../..");
@@ -82083,7 +82074,7 @@ async function loadConversationsFromParquet(parquetPath) {
82083
82074
  }
82084
82075
  function loadExistingClassifications(outputPath) {
82085
82076
  const classifiedIds = /* @__PURE__ */ new Set();
82086
- if (!existsSync9(outputPath)) {
82077
+ if (!existsSync8(outputPath)) {
82087
82078
  return classifiedIds;
82088
82079
  }
82089
82080
  const content = readFileSync7(outputPath, "utf-8");
@@ -82201,7 +82192,7 @@ async function faqClassify(ctx, options) {
82201
82192
  ctx.output.data(` Dry run: ${options.dryRun ?? false}`);
82202
82193
  ctx.output.data("");
82203
82194
  }
82204
- if (!existsSync9(parquetPath)) {
82195
+ if (!existsSync8(parquetPath)) {
82205
82196
  handleFaqClassifyError(
82206
82197
  ctx,
82207
82198
  new CLIError({
@@ -82212,7 +82203,7 @@ async function faqClassify(ctx, options) {
82212
82203
  );
82213
82204
  return;
82214
82205
  }
82215
- if (!existsSync9(taxonomyPath)) {
82206
+ if (!existsSync8(taxonomyPath)) {
82216
82207
  handleFaqClassifyError(
82217
82208
  ctx,
82218
82209
  new CLIError({
@@ -82223,9 +82214,9 @@ async function faqClassify(ctx, options) {
82223
82214
  );
82224
82215
  return;
82225
82216
  }
82226
- const outputDir = dirname3(outputPath);
82227
- if (!existsSync9(outputDir)) {
82228
- mkdirSync3(outputDir, { recursive: true });
82217
+ const outputDir = dirname2(outputPath);
82218
+ if (!existsSync8(outputDir)) {
82219
+ mkdirSync2(outputDir, { recursive: true });
82229
82220
  }
82230
82221
  if (!outputJson) ctx.output.data("\u{1F4DA} Loading taxonomy...");
82231
82222
  const taxonomy = JSON.parse(readFileSync7(taxonomyPath, "utf-8"));
@@ -82393,18 +82384,18 @@ function registerFaqClassifyCommands(program3) {
82393
82384
 
82394
82385
  // src/commands/faq/cluster.ts
82395
82386
  init_esm_shims();
82396
- import { existsSync as existsSync11 } from "fs";
82387
+ import { existsSync as existsSync10 } from "fs";
82397
82388
  import { join as join9, resolve as resolve4 } from "path";
82398
82389
 
82399
82390
  // ../core/src/faq/production-clusterer.ts
82400
82391
  init_esm_shims();
82401
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
82392
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
82402
82393
  import { join as join8 } from "path";
82403
82394
  function readPhase0Assignments(phase0Path) {
82404
82395
  const assignmentsPath = join8(phase0Path, "clusters/v1/assignments.json");
82405
- if (!existsSync10(assignmentsPath)) {
82396
+ if (!existsSync9(assignmentsPath)) {
82406
82397
  const latestPath = join8(phase0Path, "clusters/latest/assignments.json");
82407
- if (!existsSync10(latestPath)) {
82398
+ if (!existsSync9(latestPath)) {
82408
82399
  throw new Error(`Phase 0 assignments not found at ${assignmentsPath}`);
82409
82400
  }
82410
82401
  const content2 = readFileSync8(latestPath, "utf-8");
@@ -82415,9 +82406,9 @@ function readPhase0Assignments(phase0Path) {
82415
82406
  }
82416
82407
  function readPhase0Labels(phase0Path) {
82417
82408
  const labelsPath = join8(phase0Path, "clusters/v1/labels.json");
82418
- if (!existsSync10(labelsPath)) {
82409
+ if (!existsSync9(labelsPath)) {
82419
82410
  const latestPath = join8(phase0Path, "clusters/latest/labels.json");
82420
- if (!existsSync10(latestPath)) {
82411
+ if (!existsSync9(latestPath)) {
82421
82412
  throw new Error(`Phase 0 labels not found at ${labelsPath}`);
82422
82413
  }
82423
82414
  const content2 = readFileSync8(latestPath, "utf-8");
@@ -82430,9 +82421,9 @@ function readPhase0Labels(phase0Path) {
82430
82421
  }
82431
82422
  function readPhase0Metrics(phase0Path) {
82432
82423
  const metricsPath = join8(phase0Path, "clusters/v1/metrics.json");
82433
- if (!existsSync10(metricsPath)) {
82424
+ if (!existsSync9(metricsPath)) {
82434
82425
  const latestPath = join8(phase0Path, "clusters/latest/metrics.json");
82435
- if (!existsSync10(latestPath)) {
82426
+ if (!existsSync9(latestPath)) {
82436
82427
  throw new Error(`Phase 0 metrics not found at ${metricsPath}`);
82437
82428
  }
82438
82429
  return JSON.parse(readFileSync8(latestPath, "utf-8"));
@@ -82553,17 +82544,17 @@ async function generateProductionClustering(options) {
82553
82544
  }
82554
82545
  function writeProductionArtifacts(result, outputPath) {
82555
82546
  const versionPath = join8(outputPath, result.version);
82556
- if (!existsSync10(versionPath)) {
82557
- mkdirSync4(versionPath, { recursive: true });
82547
+ if (!existsSync9(versionPath)) {
82548
+ mkdirSync3(versionPath, { recursive: true });
82558
82549
  }
82559
82550
  const resultPath = join8(versionPath, "clustering-result.json");
82560
- writeFileSync7(resultPath, JSON.stringify(result, null, 2));
82551
+ writeFileSync6(resultPath, JSON.stringify(result, null, 2));
82561
82552
  console.log(`\u2705 Written: ${resultPath}`);
82562
82553
  const assignmentsPath = join8(versionPath, "assignments.json");
82563
- writeFileSync7(assignmentsPath, JSON.stringify(result.assignments, null, 2));
82554
+ writeFileSync6(assignmentsPath, JSON.stringify(result.assignments, null, 2));
82564
82555
  console.log(`\u2705 Written: ${assignmentsPath}`);
82565
82556
  const clustersPath = join8(versionPath, "clusters.json");
82566
- writeFileSync7(
82557
+ writeFileSync6(
82567
82558
  clustersPath,
82568
82559
  JSON.stringify(
82569
82560
  {
@@ -82589,14 +82580,14 @@ function writeProductionArtifacts(result, outputPath) {
82589
82580
  priorityTier: c.priorityTier
82590
82581
  }))
82591
82582
  };
82592
- writeFileSync7(summaryPath, JSON.stringify(summary, null, 2));
82583
+ writeFileSync6(summaryPath, JSON.stringify(summary, null, 2));
82593
82584
  console.log(`\u2705 Written: ${summaryPath}`);
82594
82585
  const latestPath = join8(outputPath, "latest");
82595
- if (existsSync10(latestPath)) {
82586
+ if (existsSync9(latestPath)) {
82596
82587
  const { rmSync: rmSync2 } = __require("fs");
82597
82588
  rmSync2(latestPath, { recursive: true, force: true });
82598
82589
  }
82599
- mkdirSync4(latestPath, { recursive: true });
82590
+ mkdirSync3(latestPath, { recursive: true });
82600
82591
  for (const file of [
82601
82592
  "clustering-result.json",
82602
82593
  "assignments.json",
@@ -82605,7 +82596,7 @@ function writeProductionArtifacts(result, outputPath) {
82605
82596
  ]) {
82606
82597
  const src = join8(versionPath, file);
82607
82598
  const dst = join8(latestPath, file);
82608
- writeFileSync7(dst, readFileSync8(src));
82599
+ writeFileSync6(dst, readFileSync8(src));
82609
82600
  }
82610
82601
  console.log(`\u2705 Updated: ${latestPath}`);
82611
82602
  }
@@ -82663,19 +82654,19 @@ function validatePaths(phase0Path) {
82663
82654
  const assignmentsPath = join9(phase0Path, "clusters/v1/assignments.json");
82664
82655
  const labelsPath = join9(phase0Path, "clusters/v1/labels.json");
82665
82656
  const metricsPath = join9(phase0Path, "clusters/v1/metrics.json");
82666
- if (!existsSync11(assignmentsPath)) {
82657
+ if (!existsSync10(assignmentsPath)) {
82667
82658
  throw new CLIError({
82668
82659
  userMessage: `Phase 0 assignments not found at ${assignmentsPath}.`,
82669
82660
  suggestion: "Run Phase 0 clustering first or specify the correct --phase0-path."
82670
82661
  });
82671
82662
  }
82672
- if (!existsSync11(labelsPath)) {
82663
+ if (!existsSync10(labelsPath)) {
82673
82664
  throw new CLIError({
82674
82665
  userMessage: `Phase 0 labels not found at ${labelsPath}.`,
82675
82666
  suggestion: "Verify the --phase0-path points to valid artifacts."
82676
82667
  });
82677
82668
  }
82678
- if (!existsSync11(metricsPath)) {
82669
+ if (!existsSync10(metricsPath)) {
82679
82670
  throw new CLIError({
82680
82671
  userMessage: `Phase 0 metrics not found at ${metricsPath}.`,
82681
82672
  suggestion: "Verify the --phase0-path points to valid artifacts."
@@ -82756,12 +82747,12 @@ function registerFaqClusterCommands(program3) {
82756
82747
 
82757
82748
  // src/commands/faq/extract.ts
82758
82749
  init_esm_shims();
82759
- import { existsSync as existsSync13 } from "fs";
82750
+ import { existsSync as existsSync12 } from "fs";
82760
82751
  import { join as join11, resolve as resolve5 } from "path";
82761
82752
 
82762
82753
  // ../core/src/faq/extractor.ts
82763
82754
  init_esm_shims();
82764
- import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
82755
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
82765
82756
  import { join as join10 } from "path";
82766
82757
 
82767
82758
  // ../core/src/faq/review.ts
@@ -83377,11 +83368,11 @@ async function extractFaqCandidates(options) {
83377
83368
  }
83378
83369
  function writeExtractionArtifacts(result, outputPath) {
83379
83370
  const versionPath = join10(outputPath, result.version);
83380
- if (!existsSync12(versionPath)) {
83381
- mkdirSync5(versionPath, { recursive: true });
83371
+ if (!existsSync11(versionPath)) {
83372
+ mkdirSync4(versionPath, { recursive: true });
83382
83373
  }
83383
83374
  const resultPath = join10(versionPath, "extraction-result.json");
83384
- writeFileSync8(resultPath, JSON.stringify(result, null, 2));
83375
+ writeFileSync7(resultPath, JSON.stringify(result, null, 2));
83385
83376
  console.log(`\u2705 Written: ${resultPath}`);
83386
83377
  const candidatesPath = join10(versionPath, "candidates.json");
83387
83378
  const candidatesData = {
@@ -83401,10 +83392,10 @@ function writeExtractionArtifacts(result, outputPath) {
83401
83392
  sourceConversations: c.sourceConversations.slice(0, 5)
83402
83393
  }))
83403
83394
  };
83404
- writeFileSync8(candidatesPath, JSON.stringify(candidatesData, null, 2));
83395
+ writeFileSync7(candidatesPath, JSON.stringify(candidatesData, null, 2));
83405
83396
  console.log(`\u2705 Written: ${candidatesPath}`);
83406
83397
  const statsPath = join10(versionPath, "stats.json");
83407
- writeFileSync8(
83398
+ writeFileSync7(
83408
83399
  statsPath,
83409
83400
  JSON.stringify(
83410
83401
  {
@@ -83418,11 +83409,11 @@ function writeExtractionArtifacts(result, outputPath) {
83418
83409
  );
83419
83410
  console.log(`\u2705 Written: ${statsPath}`);
83420
83411
  const latestPath = join10(outputPath, "latest");
83421
- if (existsSync12(latestPath)) {
83412
+ if (existsSync11(latestPath)) {
83422
83413
  const { rmSync: rmSync2 } = __require("fs");
83423
83414
  rmSync2(latestPath, { recursive: true, force: true });
83424
83415
  }
83425
- mkdirSync5(latestPath, { recursive: true });
83416
+ mkdirSync4(latestPath, { recursive: true });
83426
83417
  for (const file of [
83427
83418
  "extraction-result.json",
83428
83419
  "candidates.json",
@@ -83430,8 +83421,8 @@ function writeExtractionArtifacts(result, outputPath) {
83430
83421
  ]) {
83431
83422
  const src = join10(versionPath, file);
83432
83423
  const dst = join10(latestPath, file);
83433
- if (existsSync12(src)) {
83434
- writeFileSync8(dst, readFileSync9(src));
83424
+ if (existsSync11(src)) {
83425
+ writeFileSync7(dst, readFileSync9(src));
83435
83426
  }
83436
83427
  }
83437
83428
  console.log(`\u2705 Updated: ${latestPath}`);
@@ -83495,13 +83486,13 @@ var DEFAULT_GOLDEN_PATH = join11(
83495
83486
  var DEFAULT_OUTPUT_PATH3 = join11(PROJECT_ROOT3, "artifacts/phase-1/extraction");
83496
83487
  var DEFAULT_CACHE_PATH = `${process.env.HOME}/skill/data/front-cache.db`;
83497
83488
  function validatePaths2(ctx, clusteringPath, goldenPath, outputJson) {
83498
- if (!existsSync13(clusteringPath)) {
83489
+ if (!existsSync12(clusteringPath)) {
83499
83490
  throw new CLIError({
83500
83491
  userMessage: `Clustering result not found at ${clusteringPath}.`,
83501
83492
  suggestion: "Run `bun src/index.ts faq cluster` first to generate clustering."
83502
83493
  });
83503
83494
  }
83504
- if (goldenPath && !existsSync13(goldenPath)) {
83495
+ if (goldenPath && !existsSync12(goldenPath)) {
83505
83496
  if (!outputJson) {
83506
83497
  ctx.output.warn(`Golden responses not found at ${goldenPath}`);
83507
83498
  ctx.output.warn("Golden matching will be disabled.");
@@ -83530,7 +83521,7 @@ async function faqExtract(ctx, options) {
83530
83521
  ctx.output.data("");
83531
83522
  }
83532
83523
  validatePaths2(ctx, clusteringPath, goldenPath, outputJson);
83533
- if (!existsSync13(cachePath)) {
83524
+ if (!existsSync12(cachePath)) {
83534
83525
  const cliError = new CLIError({
83535
83526
  userMessage: `DuckDB cache not found at ${cachePath}.`,
83536
83527
  suggestion: "Run `bun src/index.ts front-cache sync` first to populate cache."
@@ -83552,7 +83543,7 @@ async function faqExtract(ctx, options) {
83552
83543
  }
83553
83544
  const extractionOptions = {
83554
83545
  clusteringPath,
83555
- goldenPath: existsSync13(goldenPath) ? goldenPath : void 0,
83546
+ goldenPath: existsSync12(goldenPath) ? goldenPath : void 0,
83556
83547
  source,
83557
83548
  outputPath,
83558
83549
  version,
@@ -83672,7 +83663,7 @@ function registerFaqExtractCommands(program3) {
83672
83663
 
83673
83664
  // src/commands/faq/mine.ts
83674
83665
  init_esm_shims();
83675
- import { writeFileSync as writeFileSync9 } from "fs";
83666
+ import { writeFileSync as writeFileSync8 } from "fs";
83676
83667
 
83677
83668
  // ../core/src/faq/index.ts
83678
83669
  init_esm_shims();
@@ -92489,7 +92480,7 @@ async function faqMine(ctx, options) {
92489
92480
  }))
92490
92481
  };
92491
92482
  if (options.export) {
92492
- writeFileSync9(options.export, JSON.stringify(rawData, null, 2), "utf-8");
92483
+ writeFileSync8(options.export, JSON.stringify(rawData, null, 2), "utf-8");
92493
92484
  if (outputJson) {
92494
92485
  ctx.output.data({
92495
92486
  success: true,
@@ -92563,7 +92554,7 @@ async function faqMine(ctx, options) {
92563
92554
  generatedAt: c.generatedAt.toISOString()
92564
92555
  }))
92565
92556
  };
92566
- writeFileSync9(
92557
+ writeFileSync8(
92567
92558
  options.export,
92568
92559
  JSON.stringify(exportData, null, 2),
92569
92560
  "utf-8"
@@ -92626,7 +92617,7 @@ function registerFaqMineCommands(program3) {
92626
92617
  // src/commands/faq/review.ts
92627
92618
  init_esm_shims();
92628
92619
  import { spawnSync } from "child_process";
92629
- import { existsSync as existsSync14, readFileSync as readFileSync10, unlinkSync, writeFileSync as writeFileSync10 } from "fs";
92620
+ import { existsSync as existsSync13, readFileSync as readFileSync10, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
92630
92621
  import { tmpdir } from "os";
92631
92622
  import { join as join12 } from "path";
92632
92623
  import { confirm as confirm2, select as select3 } from "@inquirer/prompts";
@@ -92704,7 +92695,7 @@ Save and close the editor when done.
92704
92695
  The sections are separated by "## Question" and "## Answer" headers.
92705
92696
  -->
92706
92697
  `;
92707
- writeFileSync10(tmpFile, content, "utf-8");
92698
+ writeFileSync9(tmpFile, content, "utf-8");
92708
92699
  try {
92709
92700
  const result = spawnSync(editor, [tmpFile], {
92710
92701
  stdio: "inherit",
@@ -92732,7 +92723,7 @@ The sections are separated by "## Question" and "## Answer" headers.
92732
92723
  answer: editedAnswer
92733
92724
  };
92734
92725
  } finally {
92735
- if (existsSync14(tmpFile)) {
92726
+ if (existsSync13(tmpFile)) {
92736
92727
  unlinkSync(tmpFile);
92737
92728
  }
92738
92729
  }
@@ -94927,7 +94918,7 @@ function registerInboxCommand(front) {
94927
94918
 
94928
94919
  // src/commands/front/pull-conversations.ts
94929
94920
  init_esm_shims();
94930
- import { writeFileSync as writeFileSync11 } from "fs";
94921
+ import { writeFileSync as writeFileSync10 } from "fs";
94931
94922
  async function pullConversations(ctx, options) {
94932
94923
  const { inbox, limit: limit2 = 50, output, filter: filter4 } = options;
94933
94924
  const outputJson = options.json === true || ctx.format === "json";
@@ -95060,7 +95051,7 @@ Built ${samples.length} eval samples`);
95060
95051
  ctx.output.data(` ${cat}: ${count}`);
95061
95052
  }
95062
95053
  if (output) {
95063
- writeFileSync11(output, JSON.stringify(samples, null, 2));
95054
+ writeFileSync10(output, JSON.stringify(samples, null, 2));
95064
95055
  ctx.output.data(`
95065
95056
  Saved to ${output}`);
95066
95057
  } else if (outputJson) {
@@ -103241,7 +103232,7 @@ var compile3 = wrapCompile(compile2);
103241
103232
  var _compileUnsafe = wrapCompile(compileUnsafe);
103242
103233
  var _compileToken = wrapCompile(compileToken);
103243
103234
  function getSelectorFunc(searchFunc) {
103244
- return function select6(query, elements, options) {
103235
+ return function select7(query, elements, options) {
103245
103236
  const opts = convertOptionFormats(options);
103246
103237
  if (typeof query !== "function") {
103247
103238
  query = compileUnsafe(query, opts, elements);
@@ -113895,10 +113886,217 @@ function registerKbCommands(program3) {
113895
113886
  });
113896
113887
  }
113897
113888
 
113898
- // src/commands/linear/index.ts
113889
+ // src/commands/keys/index.ts
113899
113890
  init_esm_shims();
113891
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
113892
+ import { password as password2, select as select5 } from "@inquirer/prompts";
113893
+ import { Decrypter as Decrypter4 } from "age-encryption";
113894
+ var buildContext4 = async (command, json) => {
113895
+ const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
113896
+ ...command.parent?.opts(),
113897
+ ...command.opts()
113898
+ };
113899
+ return createContext({
113900
+ format: json ? "json" : opts.format,
113901
+ verbose: opts.verbose,
113902
+ quiet: opts.quiet
113903
+ });
113904
+ };
113905
+ async function getUserConfiguredKeys() {
113906
+ const keyPath = getAgeKeyPath();
113907
+ const configPath = getEncryptedConfigPath();
113908
+ if (!existsSync14(keyPath) || !existsSync14(configPath)) {
113909
+ return /* @__PURE__ */ new Set();
113910
+ }
113911
+ try {
113912
+ const identity = readFileSync12(keyPath, "utf8").trim();
113913
+ const encrypted = readFileSync12(configPath);
113914
+ const decrypter = new Decrypter4();
113915
+ decrypter.addIdentity(identity);
113916
+ const decrypted = await decrypter.decrypt(encrypted, "text");
113917
+ const keys = /* @__PURE__ */ new Set();
113918
+ for (const line of decrypted.split("\n")) {
113919
+ const trimmed = line.trim();
113920
+ if (!trimmed || trimmed.startsWith("#")) continue;
113921
+ const eqIndex = trimmed.indexOf("=");
113922
+ if (eqIndex > 0) {
113923
+ keys.add(trimmed.substring(0, eqIndex));
113924
+ }
113925
+ }
113926
+ return keys;
113927
+ } catch {
113928
+ return /* @__PURE__ */ new Set();
113929
+ }
113930
+ }
113931
+ async function showKeyStatus(ctx) {
113932
+ const userKeys = await getUserConfiguredKeys();
113933
+ const allKeys = Object.keys(SECRET_REFS);
113934
+ ctx.output.data("\n\u{1F4CB} API Key Status");
113935
+ ctx.output.data("\u2500".repeat(60));
113936
+ const personal = [];
113937
+ const shared = [];
113938
+ const notSet = [];
113939
+ for (const key of allKeys) {
113940
+ const provenance = getKeyProvenance(key);
113941
+ const hasUserKey = userKeys.has(key);
113942
+ const hasEnvValue = !!process.env[key];
113943
+ if (hasUserKey) {
113944
+ personal.push(key);
113945
+ } else if (provenance === "shipped" || hasEnvValue) {
113946
+ shared.push(key);
113947
+ } else {
113948
+ notSet.push(key);
113949
+ }
113950
+ }
113951
+ if (personal.length > 0) {
113952
+ ctx.output.data("\n\u{1F510} Your personal keys:");
113953
+ for (const key of personal) {
113954
+ ctx.output.data(` \u2713 ${key}`);
113955
+ }
113956
+ }
113957
+ if (shared.length > 0) {
113958
+ ctx.output.data("\n\u{1F3E2} Using shared/shipped keys:");
113959
+ for (const key of shared.slice(0, 5)) {
113960
+ ctx.output.data(` \u2022 ${key}`);
113961
+ }
113962
+ if (shared.length > 5) {
113963
+ ctx.output.data(` \u2022 ... and ${shared.length - 5} more`);
113964
+ }
113965
+ }
113966
+ if (notSet.length > 0 && notSet.length < 10) {
113967
+ ctx.output.data("\n\u26A0\uFE0F Not configured:");
113968
+ for (const key of notSet) {
113969
+ ctx.output.data(` \u25CB ${key}`);
113970
+ }
113971
+ }
113972
+ ctx.output.data("");
113973
+ }
113974
+ async function interactiveKeySetup(ctx) {
113975
+ const keyPath = getAgeKeyPath();
113976
+ if (!existsSync14(keyPath)) {
113977
+ ctx.output.data("\n\u{1F511} First time setup - creating your encryption key...\n");
113978
+ await configInitAction(ctx, { json: false });
113979
+ ctx.output.data("");
113980
+ }
113981
+ await showKeyStatus(ctx);
113982
+ ctx.output.data("\u2500".repeat(60));
113983
+ ctx.output.data("");
113984
+ try {
113985
+ const action = await select5({
113986
+ message: "What would you like to do?",
113987
+ choices: [
113988
+ { name: "Add/update a personal API key", value: "add" },
113989
+ { name: "View all available keys", value: "list" },
113990
+ { name: "Exit", value: "exit" }
113991
+ ]
113992
+ });
113993
+ if (action === "exit") {
113994
+ return;
113995
+ }
113996
+ if (action === "list") {
113997
+ ctx.output.data("\n\u{1F4CB} Available API keys you can personalize:\n");
113998
+ const keys = Object.keys(SECRET_REFS);
113999
+ for (const key of keys) {
114000
+ ctx.output.data(` \u2022 ${key}`);
114001
+ }
114002
+ ctx.output.data("\nRun `skill keys add` to set one.");
114003
+ return;
114004
+ }
114005
+ if (action === "add") {
114006
+ const selectedKey = await select5({
114007
+ message: "Which key do you want to set?",
114008
+ choices: Object.keys(SECRET_REFS).map((key) => ({
114009
+ name: key,
114010
+ value: key
114011
+ }))
114012
+ });
114013
+ const value = await password2({
114014
+ message: `Enter your ${selectedKey}:`
114015
+ });
114016
+ if (!value) {
114017
+ ctx.output.data("Cancelled - no value provided.");
114018
+ return;
114019
+ }
114020
+ await configSetAction(ctx, `${selectedKey}=${value}`, { json: false });
114021
+ }
114022
+ } catch (error) {
114023
+ if (error instanceof Error && (error.message.includes("User force closed") || error.message.includes("canceled"))) {
114024
+ ctx.output.data("\nCancelled.");
114025
+ return;
114026
+ }
114027
+ throw error;
114028
+ }
114029
+ }
114030
+ function registerKeysCommands(program3) {
114031
+ const keys = program3.command("keys").description(
114032
+ "Manage your personal API keys\n\n Use your own API keys instead of shared defaults.\n Keys are encrypted and stored in ~/.config/skill/\n\n Examples:\n skill keys Interactive setup\n skill keys status Show which keys are personal vs shared\n skill keys add Add a personal API key"
114033
+ ).action(async (_options, command) => {
114034
+ const ctx = await buildContext4(command, false);
114035
+ if (!process.stdin.isTTY) {
114036
+ ctx.output.error(
114037
+ "Interactive mode requires a TTY. Use `skill keys add KEY=value` for scripting."
114038
+ );
114039
+ return;
114040
+ }
114041
+ await interactiveKeySetup(ctx);
114042
+ });
114043
+ keys.command("status").description("Show which API keys are personal vs shared").option("--json", "Output as JSON").action(async (options, command) => {
114044
+ const ctx = await buildContext4(command, options.json);
114045
+ if (options.json || ctx.format === "json") {
114046
+ const userKeys = await getUserConfiguredKeys();
114047
+ const allKeys = Object.keys(SECRET_REFS);
114048
+ const status = {};
114049
+ for (const key of allKeys) {
114050
+ const provenance = getKeyProvenance(key);
114051
+ if (userKeys.has(key)) {
114052
+ status[key] = "personal";
114053
+ } else if (provenance === "shipped" || process.env[key]) {
114054
+ status[key] = "shared";
114055
+ } else {
114056
+ status[key] = "not_set";
114057
+ }
114058
+ }
114059
+ ctx.output.data({ keys: status });
114060
+ } else {
114061
+ await showKeyStatus(ctx);
114062
+ }
114063
+ });
114064
+ keys.command("add [key-value]").description(
114065
+ "Add a personal API key\n\n Interactive: skill keys add\n Direct: skill keys add LINEAR_API_KEY=lin_xxx"
114066
+ ).option("--json", "Output as JSON").action(async (keyValue, options, command) => {
114067
+ const ctx = await buildContext4(command, options.json);
114068
+ const keyPath = getAgeKeyPath();
114069
+ if (!existsSync14(keyPath)) {
114070
+ if (process.stdin.isTTY && !options.json) {
114071
+ ctx.output.data(
114072
+ "\u{1F511} First time setup - creating your encryption key...\n"
114073
+ );
114074
+ }
114075
+ await configInitAction(ctx, { json: options.json });
114076
+ }
114077
+ await configSetAction(ctx, keyValue, options);
114078
+ });
114079
+ keys.command("list").description("List your personal API keys (names only, values hidden)").option("--show-values", "Show decrypted values").option("--json", "Output as JSON").action(async (options, command) => {
114080
+ const ctx = await buildContext4(command, options.json);
114081
+ const userKeys = await getUserConfiguredKeys();
114082
+ if (options.json || ctx.format === "json") {
114083
+ ctx.output.data({ personalKeys: Array.from(userKeys) });
114084
+ } else {
114085
+ if (userKeys.size === 0) {
114086
+ ctx.output.data("No personal keys configured.");
114087
+ ctx.output.data("\nRun `skill keys add` to set one.");
114088
+ } else {
114089
+ ctx.output.data("\n\u{1F510} Your personal API keys:\n");
114090
+ for (const key of userKeys) {
114091
+ ctx.output.data(` \u2022 ${key}`);
114092
+ }
114093
+ ctx.output.data("");
114094
+ }
114095
+ }
114096
+ });
114097
+ }
113900
114098
 
113901
- // src/commands/linear/assign.ts
114099
+ // src/commands/linear/index.ts
113902
114100
  init_esm_shims();
113903
114101
 
113904
114102
  // src/core/write-gate.ts
@@ -113911,11 +114109,14 @@ function requirePersonalKey(keyName) {
113911
114109
  throw new CLIError({
113912
114110
  userMessage: `Write operations require a personal API key for ${keyName}.`,
113913
114111
  exitCode: EXIT_CODES.auth,
113914
- suggestion: "Run 'skill config init' to set up your personal keys.",
114112
+ suggestion: "Run 'skill keys add' to set up your personal keys.",
113915
114113
  debugMessage: `Key provenance for ${keyName}: ${provenance ?? "undefined"}`
113916
114114
  });
113917
114115
  }
113918
114116
 
114117
+ // src/commands/linear/assign.ts
114118
+ init_esm_shims();
114119
+
113919
114120
  // src/commands/linear/client.ts
113920
114121
  init_esm_shims();
113921
114122
  import { LinearClient } from "@linear/sdk";
@@ -113947,8 +114148,8 @@ function hateoasWrap2(opts) {
113947
114148
  return response;
113948
114149
  }
113949
114150
  var WRITE_ACTION_META = {
113950
- personal_key_hint: "\u26A0\uFE0F Write operations require a personal LINEAR_API_KEY. Run `skill config init` to set up your keys.",
113951
- setup_command: "skill config init"
114151
+ personal_key_hint: "\u26A0\uFE0F Write operations require a personal LINEAR_API_KEY. Run `skill keys add` to set up your keys.",
114152
+ setup_command: "skill keys add"
113952
114153
  };
113953
114154
  function issueLinks(identifier, teamKey) {
113954
114155
  const links = [
@@ -114809,7 +115010,7 @@ async function getIssue(ctx, id) {
114809
115010
  ctx.output.data(` \u2022 Close: skill linear close ${issue.identifier}`);
114810
115011
  ctx.output.data("");
114811
115012
  ctx.output.data(" \u26A0\uFE0F Write operations require a personal LINEAR_API_KEY.");
114812
- ctx.output.data(" Run `skill config init` to set up your keys.");
115013
+ ctx.output.data(" Run `skill keys add` to set up your keys.");
114813
115014
  ctx.output.data("");
114814
115015
  } catch (error) {
114815
115016
  const cliError = error instanceof CLIError ? error : new CLIError({
@@ -116117,7 +116318,9 @@ Quick start:
116117
116318
  skill linear create "Title" Create issue
116118
116319
  skill linear search "query" Search issues
116119
116320
 
116120
- All commands support --json for machine-readable output.`
116321
+ All commands support --json for machine-readable output.
116322
+
116323
+ \u26A0\uFE0F Write operations require personal LINEAR_API_KEY (run 'skill keys add').`
116121
116324
  );
116122
116325
  linear.command("issues").description(
116123
116326
  `List issues with optional filters
@@ -116147,6 +116350,7 @@ Examples:
116147
116350
  skill linear my --state "In Progress" Only in-progress
116148
116351
  skill linear my --limit 5 Just top 5`
116149
116352
  ).option("--limit <number>", "Maximum results (default: 20)", "20").option("--state <name>", "Filter by state name").option("--json", "Output as JSON with HATEOAS links").action(async (options, command) => {
116353
+ requirePersonalKey("LINEAR_API_KEY");
116150
116354
  const ctx = await contextFromCommand2(command, options);
116151
116355
  await listMyIssues(ctx, {
116152
116356
  limit: parseInt(options.limit || "20", 10),
@@ -116191,6 +116395,7 @@ Priority: 0=Urgent, 1=High, 2=Medium, 3=Low, 4=None`
116191
116395
  (v, p) => [...p, v],
116192
116396
  []
116193
116397
  ).option("--project <name>", "Project name").option("--estimate <points>", "Estimate in points").option("--due-date <YYYY-MM-DD>", "Due date").option("--json", "Output as JSON with HATEOAS links").action(async (title, options, command) => {
116398
+ requirePersonalKey("LINEAR_API_KEY");
116194
116399
  const ctx = await contextFromCommand2(command, options);
116195
116400
  await createIssue(ctx, title, {
116196
116401
  description: options.description,
@@ -116211,6 +116416,7 @@ Examples:
116211
116416
  skill linear update ENG-123 --priority 1 --estimate 3
116212
116417
  skill linear update ENG-123 --due-date 2024-03-15`
116213
116418
  ).argument("<id>", "Issue identifier").option("--title <text>", "New title").option("--description <text>", "New description").option("--priority <0-4>", "New priority").option("--estimate <points>", "New estimate").option("--due-date <YYYY-MM-DD>", "New due date").option("--project <name>", "Move to project").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116419
+ requirePersonalKey("LINEAR_API_KEY");
116214
116420
  const ctx = await contextFromCommand2(command, options);
116215
116421
  await updateIssue(ctx, id, {
116216
116422
  title: options.title,
@@ -116229,6 +116435,7 @@ Examples:
116229
116435
  skill linear assign ENG-123 --to me
116230
116436
  skill linear assign ENG-123 --unassign`
116231
116437
  ).argument("<id>", "Issue identifier").option("--to <email>", 'Assign to user email (or "me")').option("--unassign", "Remove assignee").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116438
+ requirePersonalKey("LINEAR_API_KEY");
116232
116439
  const ctx = await contextFromCommand2(command, options);
116233
116440
  await assignIssue(ctx, id, {
116234
116441
  to: options.to,
@@ -116244,6 +116451,7 @@ Examples:
116244
116451
 
116245
116452
  Use 'skill linear states <team>' to see available states.`
116246
116453
  ).argument("<id>", "Issue identifier").requiredOption("--state <name>", "Target state name").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116454
+ requirePersonalKey("LINEAR_API_KEY");
116247
116455
  const ctx = await contextFromCommand2(command, options);
116248
116456
  await changeState(ctx, id, { state: options.state });
116249
116457
  });
@@ -116254,6 +116462,7 @@ Examples:
116254
116462
  skill linear close ENG-123 Close as done
116255
116463
  skill linear close ENG-123 --canceled Cancel the issue`
116256
116464
  ).argument("<id>", "Issue identifier").option("--canceled", "Close as canceled instead of done").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116465
+ requirePersonalKey("LINEAR_API_KEY");
116257
116466
  const ctx = await contextFromCommand2(command, options);
116258
116467
  await closeIssue(ctx, id, { canceled: options.canceled });
116259
116468
  });
@@ -116277,6 +116486,7 @@ Use 'skill linear labels <team>' to see available labels.`
116277
116486
  (v, p) => [...p, v],
116278
116487
  []
116279
116488
  ).option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116489
+ requirePersonalKey("LINEAR_API_KEY");
116280
116490
  const ctx = await contextFromCommand2(command, options);
116281
116491
  await modifyLabels(ctx, id, {
116282
116492
  add: options.add,
@@ -116292,6 +116502,7 @@ Examples:
116292
116502
  skill linear link ENG-123 --related ENG-456
116293
116503
  skill linear link ENG-123 --duplicate ENG-456`
116294
116504
  ).argument("<id>", "Source issue identifier").option("--blocks <id>", "This issue blocks <id>").option("--blocked-by <id>", "This issue is blocked by <id>").option("--related <id>", "Related to <id>").option("--duplicate <id>", "Duplicate of <id>").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116505
+ requirePersonalKey("LINEAR_API_KEY");
116295
116506
  const ctx = await contextFromCommand2(command, options);
116296
116507
  await linkIssues(ctx, id, {
116297
116508
  blocks: options.blocks,
@@ -116307,6 +116518,7 @@ Examples:
116307
116518
  skill linear comment ENG-123 --body "Great work!"
116308
116519
  skill linear comment ENG-123 --body "## Update\\n- Item 1\\n- Item 2"`
116309
116520
  ).argument("<id>", "Issue identifier").requiredOption("--body <text>", "Comment text (supports markdown)").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
116521
+ requirePersonalKey("LINEAR_API_KEY");
116310
116522
  const ctx = await contextFromCommand2(command, options);
116311
116523
  await addComment(ctx, id, { body: options.body });
116312
116524
  });
@@ -116738,7 +116950,7 @@ async function deleteMemory(ctx, id, options) {
116738
116950
  }
116739
116951
 
116740
116952
  // src/commands/memory/index.ts
116741
- var buildContext4 = async (command, json) => {
116953
+ var buildContext5 = async (command, json) => {
116742
116954
  const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
116743
116955
  ...command.parent?.opts(),
116744
116956
  ...command.opts()
@@ -116752,42 +116964,42 @@ var buildContext4 = async (command, json) => {
116752
116964
  function registerMemoryCommands(program3) {
116753
116965
  const memory = program3.command("memory").description("Manage semantic memory for agent learning");
116754
116966
  memory.command("store").description("Store a new memory").argument("<content>", "Memory content to store").option("--tags <tags>", "Comma-separated tags").option("--collection <collection>", "Collection name (default: learnings)").option("--app <app>", "App slug to associate with memory").option("--json", "Output as JSON").action(async (content, options, command) => {
116755
- const ctx = await buildContext4(command, options.json);
116967
+ const ctx = await buildContext5(command, options.json);
116756
116968
  await store(ctx, content, options);
116757
116969
  });
116758
116970
  memory.command("find").description("Search memories by semantic similarity").argument("<query>", "Search query text").option("--limit <number>", "Max results (1-100, default: 10)").option("--collection <collection>", "Collection name (default: learnings)").option("--app <app>", "Filter by app slug").option(
116759
116971
  "--min-confidence <confidence>",
116760
116972
  "Minimum confidence threshold (0-1, default: 0.5)"
116761
116973
  ).option("--json", "Output as JSON").action(async (query, options, command) => {
116762
- const ctx = await buildContext4(command, options.json);
116974
+ const ctx = await buildContext5(command, options.json);
116763
116975
  await find5(ctx, query, options);
116764
116976
  });
116765
116977
  memory.command("get").description("Get a specific memory by ID").argument("<id>", "Memory ID").option("--collection <collection>", "Collection name (default: learnings)").option("--json", "Output as JSON").action(async (id, options, command) => {
116766
- const ctx = await buildContext4(command, options.json);
116978
+ const ctx = await buildContext5(command, options.json);
116767
116979
  await get2(ctx, id, options);
116768
116980
  });
116769
116981
  memory.command("validate").description("Validate a memory (resets decay clock)").argument("<id>", "Memory ID").option("--collection <collection>", "Collection name (default: learnings)").option("--json", "Output as JSON").action(async (id, options, command) => {
116770
- const ctx = await buildContext4(command, options.json);
116982
+ const ctx = await buildContext5(command, options.json);
116771
116983
  await validate2(ctx, id, options);
116772
116984
  });
116773
116985
  memory.command("upvote").description("Upvote a memory").argument("<id>", "Memory ID").option("--collection <collection>", "Collection name (default: learnings)").option("--reason <reason>", "Optional reason for upvote").option("--json", "Output as JSON").action(async (id, options, command) => {
116774
- const ctx = await buildContext4(command, options.json);
116986
+ const ctx = await buildContext5(command, options.json);
116775
116987
  await upvote(ctx, id, options);
116776
116988
  });
116777
116989
  memory.command("downvote").description("Downvote a memory").argument("<id>", "Memory ID").option("--collection <collection>", "Collection name (default: learnings)").option("--reason <reason>", "Optional reason for downvote").option("--json", "Output as JSON").action(async (id, options, command) => {
116778
- const ctx = await buildContext4(command, options.json);
116990
+ const ctx = await buildContext5(command, options.json);
116779
116991
  await downvote(ctx, id, options);
116780
116992
  });
116781
116993
  memory.command("delete").description("Delete a memory").argument("<id>", "Memory ID").option("--collection <collection>", "Collection name (default: learnings)").option("--json", "Output as JSON").action(async (id, options, command) => {
116782
- const ctx = await buildContext4(command, options.json);
116994
+ const ctx = await buildContext5(command, options.json);
116783
116995
  await deleteMemory(ctx, id, options);
116784
116996
  });
116785
116997
  memory.command("stats").description("Display memory statistics").option("--collection <collection>", "Filter by collection").option("--app <app>", "Filter by app slug").option("--json", "Output as JSON").action(async (options, command) => {
116786
- const ctx = await buildContext4(command, options.json);
116998
+ const ctx = await buildContext5(command, options.json);
116787
116999
  await stats3(ctx, options);
116788
117000
  });
116789
117001
  memory.command("stale").description("List stale memories needing validation").option("--collection <collection>", "Filter by collection").option("--threshold <threshold>", "Confidence threshold (default: 0.25)").option("--json", "Output as JSON").action(async (options, command) => {
116790
- const ctx = await buildContext4(command, options.json);
117002
+ const ctx = await buildContext5(command, options.json);
116791
117003
  await stale(ctx, options);
116792
117004
  });
116793
117005
  }
@@ -117369,7 +117581,7 @@ async function buildValidateDatasetFromProduction(productionResultsPath, outputP
117369
117581
  }
117370
117582
 
117371
117583
  // src/commands/pipeline.ts
117372
- var buildContext5 = async (command, json) => {
117584
+ var buildContext6 = async (command, json) => {
117373
117585
  const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
117374
117586
  ...command.parent?.opts(),
117375
117587
  ...command.opts()
@@ -117440,7 +117652,7 @@ function registerPipelineCommands(program3) {
117440
117652
  "Model for LLM classification",
117441
117653
  "anthropic/claude-haiku-4-5"
117442
117654
  ).action(async (opts, command) => {
117443
- const ctx = await buildContext5(command, opts.json);
117655
+ const ctx = await buildContext6(command, opts.json);
117444
117656
  try {
117445
117657
  await runClassifyEval2({
117446
117658
  ...opts,
@@ -117451,7 +117663,7 @@ function registerPipelineCommands(program3) {
117451
117663
  }
117452
117664
  });
117453
117665
  pipeline.command("build-classify-dataset").description("Build classify eval dataset from production data").requiredOption("--production <file>", "Production dataset JSON").requiredOption("--output <file>", "Output scenarios JSON").action(async (opts, command) => {
117454
- const ctx = await buildContext5(command);
117666
+ const ctx = await buildContext6(command);
117455
117667
  try {
117456
117668
  await buildClassifyDataset(opts.production, opts.output);
117457
117669
  ctx.output.success(`Dataset written to ${opts.output}`);
@@ -117463,7 +117675,7 @@ function registerPipelineCommands(program3) {
117463
117675
  "--dataset <file>",
117464
117676
  "Path to scenarios JSON (uses built-in if not provided)"
117465
117677
  ).option("--output <file>", "Save results to JSON").option("--verbose", "Show individual failures").option("--json", "JSON output").action(async (opts, command) => {
117466
- const ctx = await buildContext5(command, opts.json);
117678
+ const ctx = await buildContext6(command, opts.json);
117467
117679
  try {
117468
117680
  await runValidateEval2({
117469
117681
  ...opts,
@@ -117474,7 +117686,7 @@ function registerPipelineCommands(program3) {
117474
117686
  }
117475
117687
  });
117476
117688
  pipeline.command("build-validate-dataset").description("Build validate eval dataset from production failures").requiredOption("--production <file>", "Production baseline results JSON").requiredOption("--output <file>", "Output scenarios JSON").action(async (opts, command) => {
117477
- const ctx = await buildContext5(command);
117689
+ const ctx = await buildContext6(command);
117478
117690
  try {
117479
117691
  await buildValidateDatasetFromProduction(opts.production, opts.output);
117480
117692
  ctx.output.success(`Dataset written to ${opts.output}`);
@@ -117487,7 +117699,7 @@ function registerPipelineCommands(program3) {
117487
117699
  "Model for LLM steps",
117488
117700
  "anthropic/claude-haiku-4-5"
117489
117701
  ).action(async (opts, command) => {
117490
- const ctx = await buildContext5(command, opts.json);
117702
+ const ctx = await buildContext6(command, opts.json);
117491
117703
  try {
117492
117704
  await runE2EEval2({ ...opts, json: opts.json ?? ctx.format === "json" });
117493
117705
  } catch (error) {
@@ -117495,7 +117707,7 @@ function registerPipelineCommands(program3) {
117495
117707
  }
117496
117708
  });
117497
117709
  pipeline.command("run").description("Run pipeline on a single message").requiredOption("--subject <text>", "Message subject").requiredOption("--body <text>", "Message body").option("--app <id>", "App ID", "total-typescript").option("--dry-run", "Don't actually send", true).option("--json", "JSON output").action(async (opts, command) => {
117498
- const ctx = await buildContext5(command, opts.json);
117710
+ const ctx = await buildContext6(command, opts.json);
117499
117711
  await runPipelineCommand(ctx, opts);
117500
117712
  });
117501
117713
  }
@@ -117503,10 +117715,10 @@ function registerPipelineCommands(program3) {
117503
117715
  // src/commands/plugin-sync.ts
117504
117716
  init_esm_shims();
117505
117717
  import { homedir as homedir3 } from "os";
117506
- import { dirname as dirname4, join as join13, resolve as resolve6 } from "path";
117718
+ import { dirname as dirname3, join as join13, resolve as resolve6 } from "path";
117507
117719
  import { fileURLToPath } from "url";
117508
117720
  var PLUGIN_SOURCE_DIR = resolve6(
117509
- dirname4(fileURLToPath(import.meta.url)),
117721
+ dirname3(fileURLToPath(import.meta.url)),
117510
117722
  "../../plugin"
117511
117723
  );
117512
117724
  var PLUGIN_MANIFEST_RELATIVE = join13(".claude-plugin", "plugin.json");
@@ -117612,7 +117824,7 @@ var registerPluginSyncCommand = (program3) => {
117612
117824
 
117613
117825
  // src/commands/responses.ts
117614
117826
  init_esm_shims();
117615
- import { writeFileSync as writeFileSync12 } from "fs";
117827
+ import { writeFileSync as writeFileSync11 } from "fs";
117616
117828
  function formatDate3(date) {
117617
117829
  return date.toLocaleString("en-US", {
117618
117830
  month: "short",
@@ -118145,7 +118357,7 @@ async function exportResponses(ctx, options) {
118145
118357
  }
118146
118358
  const outputJson = JSON.stringify(exportData, null, 2);
118147
118359
  if (options.output) {
118148
- writeFileSync12(options.output, outputJson, "utf-8");
118360
+ writeFileSync11(options.output, outputJson, "utf-8");
118149
118361
  if (!outputJsonFormat) {
118150
118362
  ctx.output.success(
118151
118363
  `Exported ${exportData.length} responses to ${options.output}`
@@ -118794,7 +119006,7 @@ init_esm_shims();
118794
119006
  import { spawn } from "child_process";
118795
119007
  import { writeFile as writeFile7 } from "fs/promises";
118796
119008
  import { homedir as homedir4 } from "os";
118797
- import { dirname as dirname5, join as join14 } from "path";
119009
+ import { dirname as dirname4, join as join14 } from "path";
118798
119010
  var CONFIG_DIR_NAME = "skill-cli";
118799
119011
  var AUTO_UPDATE_STATE_FILE = "auto-update.json";
118800
119012
  var DEFAULT_PACKAGE = "@skillrecordings/cli";
@@ -118824,7 +119036,7 @@ var AutoUpdateStore = class {
118824
119036
  }
118825
119037
  async save(state) {
118826
119038
  try {
118827
- await ensureDir(dirname5(this.filePath));
119039
+ await ensureDir(dirname4(this.filePath));
118828
119040
  await writeFile7(this.filePath, JSON.stringify(state, null, 2), "utf-8");
118829
119041
  } catch {
118830
119042
  }
@@ -119030,7 +119242,7 @@ var DEFAULT_HINT_RULES = [
119030
119242
  {
119031
119243
  id: "onboarding.auth",
119032
119244
  audience: "onboarding",
119033
- message: "Set up credentials with `skill auth setup` (requires 1Password).",
119245
+ message: "Set up your own API keys with `skill keys`.",
119034
119246
  showWhen: (state) => state.totalRuns >= 1 && !hasMilestone(state, "auth_configured"),
119035
119247
  retireWhen: (state) => hasMilestone(state, "auth_configured")
119036
119248
  },
@@ -119062,6 +119274,13 @@ var DEFAULT_HINT_RULES = [
119062
119274
  showWhen: (state) => state.totalRuns >= 3 && !hasCommandPrefix(state, "axiom."),
119063
119275
  retireWhen: (state) => hasCommandPrefix(state, "axiom.")
119064
119276
  },
119277
+ {
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")
119283
+ },
119065
119284
  {
119066
119285
  id: "context.front.triage",
119067
119286
  audience: "contextual",
@@ -119132,10 +119351,10 @@ var writeHints = (hints, stderr) => {
119132
119351
  init_esm_shims();
119133
119352
  import { lstat, readlink, symlink } from "fs/promises";
119134
119353
  import { homedir as homedir5 } from "os";
119135
- import { dirname as dirname6, join as join15, resolve as resolve7 } from "path";
119354
+ import { dirname as dirname5, join as join15, resolve as resolve7 } from "path";
119136
119355
  import { fileURLToPath as fileURLToPath2 } from "url";
119137
119356
  var SKILL_SOURCE_DIR = resolve7(
119138
- dirname6(fileURLToPath2(import.meta.url)),
119357
+ dirname5(fileURLToPath2(import.meta.url)),
119139
119358
  "../../../../.claude/skills/skill-cli"
119140
119359
  );
119141
119360
  var SKILL_TARGET_DIR = join15(homedir5(), ".claude", "skills", "skill-cli");
@@ -119149,7 +119368,7 @@ async function autoLinkSkill() {
119149
119368
  } catch (e) {
119150
119369
  if (e.code === "ENOENT") {
119151
119370
  const { mkdir: mkdir2 } = await import("fs/promises");
119152
- await mkdir2(dirname6(target), { recursive: true });
119371
+ await mkdir2(dirname5(target), { recursive: true });
119153
119372
  await symlink(source, target, "dir");
119154
119373
  return {
119155
119374
  status: "linked",
@@ -119162,7 +119381,7 @@ async function autoLinkSkill() {
119162
119381
  }
119163
119382
  if (stats4.isSymbolicLink()) {
119164
119383
  const linkTarget = await readlink(target);
119165
- const resolvedLinkTarget = resolve7(dirname6(target), linkTarget);
119384
+ const resolvedLinkTarget = resolve7(dirname5(target), linkTarget);
119166
119385
  if (resolvedLinkTarget === source || linkTarget === source) {
119167
119386
  return {
119168
119387
  status: "exists",
@@ -119242,7 +119461,7 @@ async function sendTelemetryEvent(event) {
119242
119461
  init_esm_shims();
119243
119462
  import { writeFile as writeFile8 } from "fs/promises";
119244
119463
  import { homedir as homedir6 } from "os";
119245
- import { dirname as dirname7, join as join16 } from "path";
119464
+ import { dirname as dirname6, join as join16 } from "path";
119246
119465
  var CONFIG_DIR_NAME2 = "skill-cli";
119247
119466
  var USAGE_FILE_NAME = "usage.json";
119248
119467
  function resolveConfigDir2(configDir) {
@@ -119312,7 +119531,7 @@ var UsageTracker = class {
119312
119531
  }
119313
119532
  async saveState(state) {
119314
119533
  try {
119315
- await ensureDir(dirname7(this.filePath));
119534
+ await ensureDir(dirname6(this.filePath));
119316
119535
  await writeFile8(this.filePath, JSON.stringify(state, null, 2), "utf-8");
119317
119536
  } catch {
119318
119537
  }
@@ -119868,8 +120087,8 @@ if (!envLoaded && !process.env.DATABASE_URL) {
119868
120087
  process.env.SKIP_ENV_VALIDATION = "1";
119869
120088
  }
119870
120089
  var runtimeTarget = `bun-${process.platform}-${process.arch}`;
119871
- var buildVersion = "0.15.0".length > 0 ? "0.15.0" : "0.0.0-dev";
119872
- var buildCommit = "73ba4fa".length > 0 ? "73ba4fa" : "dev";
120090
+ var buildVersion = "0.16.1".length > 0 ? "0.16.1" : "0.0.0-dev";
120091
+ var buildCommit = "7f0ef62".length > 0 ? "7f0ef62" : "dev";
119873
120092
  var buildTarget = "node".length > 0 ? "node" : runtimeTarget;
119874
120093
  var isDevBuild = buildVersion.includes("dev") || buildCommit === "dev";
119875
120094
  var versionLabel = `skill v${buildVersion} (${buildCommit}) ${buildTarget}`;
@@ -120088,6 +120307,7 @@ registerDeployCommands(program2);
120088
120307
  registerKbCommands(program2);
120089
120308
  registerAuthCommands(program2, usageState);
120090
120309
  registerConfigCommands(program2);
120310
+ registerKeysCommands(program2);
120091
120311
  registerPluginSyncCommand(program2);
120092
120312
  program2.command("mcp").description(
120093
120313
  "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)"