@skillrecordings/cli 0.11.2 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/index.js +738 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -19837,8 +19837,8 @@ var require_snapshot_recorder = __commonJS({
|
|
|
19837
19837
|
"../../node_modules/.bun/undici@7.20.0/node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
|
|
19838
19838
|
"use strict";
|
|
19839
19839
|
init_esm_shims();
|
|
19840
|
-
var { writeFile:
|
|
19841
|
-
var { dirname:
|
|
19840
|
+
var { writeFile: writeFile9, readFile: readFile10, mkdir: mkdir2 } = __require("fs/promises");
|
|
19841
|
+
var { dirname: dirname6, resolve: resolve7 } = __require("path");
|
|
19842
19842
|
var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("timers");
|
|
19843
19843
|
var { InvalidArgumentError, UndiciError } = require_errors();
|
|
19844
19844
|
var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
|
|
@@ -20069,12 +20069,12 @@ var require_snapshot_recorder = __commonJS({
|
|
|
20069
20069
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
20070
20070
|
}
|
|
20071
20071
|
const resolvedPath = resolve7(path);
|
|
20072
|
-
await mkdir2(
|
|
20072
|
+
await mkdir2(dirname6(resolvedPath), { recursive: true });
|
|
20073
20073
|
const data2 = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
|
|
20074
20074
|
hash,
|
|
20075
20075
|
snapshot
|
|
20076
20076
|
}));
|
|
20077
|
-
await
|
|
20077
|
+
await writeFile9(resolvedPath, JSON.stringify(data2, null, 2), { flush: true });
|
|
20078
20078
|
}
|
|
20079
20079
|
/**
|
|
20080
20080
|
* Clears all recorded snapshots
|
|
@@ -74544,6 +74544,63 @@ import { Command as Command4 } from "commander";
|
|
|
74544
74544
|
// src/commands/auth/index.ts
|
|
74545
74545
|
init_esm_shims();
|
|
74546
74546
|
|
|
74547
|
+
// src/core/adaptive-help.ts
|
|
74548
|
+
init_esm_shims();
|
|
74549
|
+
var ABBREVIATED_THRESHOLD = 2;
|
|
74550
|
+
var MINIMAL_THRESHOLD = 5;
|
|
74551
|
+
var ROOT_DESCRIPTIONS = {
|
|
74552
|
+
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",
|
|
74553
|
+
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",
|
|
74554
|
+
minimal: "Skill Recordings support agent CLI. Try: skill front inbox, skill front triage, skill auth status. Use --help for details."
|
|
74555
|
+
};
|
|
74556
|
+
var FRONT_DESCRIPTIONS = {
|
|
74557
|
+
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.",
|
|
74558
|
+
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).",
|
|
74559
|
+
minimal: "Front API commands (inbox, triage, assign, reply, archive). Requires FRONT_API_TOKEN."
|
|
74560
|
+
};
|
|
74561
|
+
var AUTH_DESCRIPTIONS = {
|
|
74562
|
+
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.",
|
|
74563
|
+
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`).",
|
|
74564
|
+
minimal: "Auth and 1Password setup commands."
|
|
74565
|
+
};
|
|
74566
|
+
var INNGEST_DESCRIPTIONS = {
|
|
74567
|
+
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",
|
|
74568
|
+
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.",
|
|
74569
|
+
minimal: "Inngest events and runs debugging. Requires INNGEST_SIGNING_KEY and INNGEST_EVENT_KEY."
|
|
74570
|
+
};
|
|
74571
|
+
var GROUP_DESCRIPTIONS = {
|
|
74572
|
+
root: ROOT_DESCRIPTIONS,
|
|
74573
|
+
front: FRONT_DESCRIPTIONS,
|
|
74574
|
+
auth: AUTH_DESCRIPTIONS,
|
|
74575
|
+
inngest: INNGEST_DESCRIPTIONS
|
|
74576
|
+
};
|
|
74577
|
+
var getGroupCommandCount = (state, group) => {
|
|
74578
|
+
if (group === "root") return state.totalRuns;
|
|
74579
|
+
const prefix = `${group}.`;
|
|
74580
|
+
let total = 0;
|
|
74581
|
+
for (const [name, entry] of Object.entries(state.commands)) {
|
|
74582
|
+
if (name === group || name.startsWith(prefix)) {
|
|
74583
|
+
total += entry.count;
|
|
74584
|
+
}
|
|
74585
|
+
}
|
|
74586
|
+
return total;
|
|
74587
|
+
};
|
|
74588
|
+
var resolveProficiencyLevel = (state, group) => {
|
|
74589
|
+
if (!state) return "full";
|
|
74590
|
+
const count = getGroupCommandCount(state, group);
|
|
74591
|
+
if (count >= MINIMAL_THRESHOLD) return "minimal";
|
|
74592
|
+
if (count >= ABBREVIATED_THRESHOLD) return "abbreviated";
|
|
74593
|
+
return "full";
|
|
74594
|
+
};
|
|
74595
|
+
var getAdaptiveDescription = (group, state) => {
|
|
74596
|
+
const level = resolveProficiencyLevel(state, group);
|
|
74597
|
+
return GROUP_DESCRIPTIONS[group][level];
|
|
74598
|
+
};
|
|
74599
|
+
var getRootAdaptiveDescription = (state) => getAdaptiveDescription("root", state);
|
|
74600
|
+
var getFrontAdaptiveDescription = (state) => getAdaptiveDescription("front", state);
|
|
74601
|
+
var getAuthAdaptiveDescription = (state) => getAdaptiveDescription("auth", state);
|
|
74602
|
+
var getInngestAdaptiveDescription = (state) => getAdaptiveDescription("inngest", state);
|
|
74603
|
+
|
|
74547
74604
|
// src/core/context.ts
|
|
74548
74605
|
init_esm_shims();
|
|
74549
74606
|
|
|
@@ -75582,8 +75639,8 @@ var buildContext = async (command, json) => {
|
|
|
75582
75639
|
quiet: opts.quiet
|
|
75583
75640
|
});
|
|
75584
75641
|
};
|
|
75585
|
-
function registerAuthCommands(program3) {
|
|
75586
|
-
const auth = program3.command("auth").description(
|
|
75642
|
+
function registerAuthCommands(program3, usageState2) {
|
|
75643
|
+
const auth = program3.command("auth").description(getAuthAdaptiveDescription(usageState2));
|
|
75587
75644
|
auth.command("status").description("Show active auth provider and secret availability").option("--json", "Output as JSON").action(async (options, command) => {
|
|
75588
75645
|
const ctx = await buildContext(command, options.json);
|
|
75589
75646
|
await statusAction(ctx, options);
|
|
@@ -96087,8 +96144,8 @@ async function getTeammate(ctx, id, options) {
|
|
|
96087
96144
|
process.exitCode = cliError.exitCode;
|
|
96088
96145
|
}
|
|
96089
96146
|
}
|
|
96090
|
-
function registerFrontCommands(program3) {
|
|
96091
|
-
const front = program3.command("front").description(
|
|
96147
|
+
function registerFrontCommands(program3, usageState2) {
|
|
96148
|
+
const front = program3.command("front").description(getFrontAdaptiveDescription(usageState2));
|
|
96092
96149
|
front.command("message").description("Get a message by ID (body, author, recipients, attachments)").argument("<id>", "Message ID (e.g., msg_xxx)").option("--json", "Output as JSON").action(
|
|
96093
96150
|
async (id, options, command) => {
|
|
96094
96151
|
const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
|
|
@@ -97377,8 +97434,8 @@ function registerSignalCommand(inngest) {
|
|
|
97377
97434
|
}
|
|
97378
97435
|
|
|
97379
97436
|
// src/commands/inngest/index.ts
|
|
97380
|
-
function registerInngestCommands(program3) {
|
|
97381
|
-
const inngest = program3.command("inngest").description(
|
|
97437
|
+
function registerInngestCommands(program3, usageState2) {
|
|
97438
|
+
const inngest = program3.command("inngest").description(getInngestAdaptiveDescription(usageState2));
|
|
97382
97439
|
registerEventsCommands(inngest);
|
|
97383
97440
|
registerRunsCommands(inngest);
|
|
97384
97441
|
registerSignalCommand(inngest);
|
|
@@ -115844,6 +115901,529 @@ async function wizard(ctx, options = {}) {
|
|
|
115844
115901
|
}
|
|
115845
115902
|
}
|
|
115846
115903
|
|
|
115904
|
+
// src/core/auto-update.ts
|
|
115905
|
+
init_esm_shims();
|
|
115906
|
+
import { spawn } from "child_process";
|
|
115907
|
+
import { writeFile as writeFile7 } from "fs/promises";
|
|
115908
|
+
import { homedir as homedir2 } from "os";
|
|
115909
|
+
import { dirname as dirname4, join as join12 } from "path";
|
|
115910
|
+
var CONFIG_DIR_NAME = "skill-cli";
|
|
115911
|
+
var AUTO_UPDATE_STATE_FILE = "auto-update.json";
|
|
115912
|
+
var DEFAULT_PACKAGE = "@skillrecordings/cli";
|
|
115913
|
+
var REGISTRY_BASE = "https://registry.npmjs.org/";
|
|
115914
|
+
var CHECK_THROTTLE_MS = 60 * 60 * 1e3;
|
|
115915
|
+
var UPDATE_THROTTLE_MS = 24 * 60 * 60 * 1e3;
|
|
115916
|
+
var AutoUpdateStore = class {
|
|
115917
|
+
filePath;
|
|
115918
|
+
now;
|
|
115919
|
+
constructor(options = {}) {
|
|
115920
|
+
const configDir = resolveConfigDir(options.configDir);
|
|
115921
|
+
this.filePath = join12(configDir, AUTO_UPDATE_STATE_FILE);
|
|
115922
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
115923
|
+
}
|
|
115924
|
+
getNow() {
|
|
115925
|
+
return this.now();
|
|
115926
|
+
}
|
|
115927
|
+
async load() {
|
|
115928
|
+
try {
|
|
115929
|
+
if (await pathExists(this.filePath)) {
|
|
115930
|
+
const data2 = await readJson(this.filePath);
|
|
115931
|
+
if (isAutoUpdateState(data2)) return data2;
|
|
115932
|
+
}
|
|
115933
|
+
} catch {
|
|
115934
|
+
}
|
|
115935
|
+
return {};
|
|
115936
|
+
}
|
|
115937
|
+
async save(state) {
|
|
115938
|
+
try {
|
|
115939
|
+
await ensureDir(dirname4(this.filePath));
|
|
115940
|
+
await writeFile7(this.filePath, JSON.stringify(state, null, 2), "utf-8");
|
|
115941
|
+
} catch {
|
|
115942
|
+
}
|
|
115943
|
+
}
|
|
115944
|
+
};
|
|
115945
|
+
function resolveConfigDir(configDir) {
|
|
115946
|
+
if (configDir) return configDir;
|
|
115947
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
115948
|
+
if (xdgConfigHome && xdgConfigHome.trim() !== "") {
|
|
115949
|
+
return join12(xdgConfigHome, CONFIG_DIR_NAME);
|
|
115950
|
+
}
|
|
115951
|
+
return join12(homedir2(), ".config", CONFIG_DIR_NAME);
|
|
115952
|
+
}
|
|
115953
|
+
function isAutoUpdateState(value) {
|
|
115954
|
+
if (!value || typeof value !== "object") return false;
|
|
115955
|
+
const state = value;
|
|
115956
|
+
if (state.lastCheckAt !== void 0 && typeof state.lastCheckAt !== "string") {
|
|
115957
|
+
return false;
|
|
115958
|
+
}
|
|
115959
|
+
if (state.lastUpdateAt !== void 0 && typeof state.lastUpdateAt !== "string") {
|
|
115960
|
+
return false;
|
|
115961
|
+
}
|
|
115962
|
+
if (state.lastKnownVersion !== void 0 && typeof state.lastKnownVersion !== "string") {
|
|
115963
|
+
return false;
|
|
115964
|
+
}
|
|
115965
|
+
return true;
|
|
115966
|
+
}
|
|
115967
|
+
function isAutoUpdateDisabled() {
|
|
115968
|
+
return process.env.SKILL_NO_AUTO_UPDATE === "1";
|
|
115969
|
+
}
|
|
115970
|
+
function isWithinWindow(timestamp, now, windowMs) {
|
|
115971
|
+
if (!timestamp) return false;
|
|
115972
|
+
const parsed = Date.parse(timestamp);
|
|
115973
|
+
if (Number.isNaN(parsed)) return false;
|
|
115974
|
+
return now.getTime() - parsed < windowMs;
|
|
115975
|
+
}
|
|
115976
|
+
function normalizePackageName(name) {
|
|
115977
|
+
if (name.startsWith("@")) {
|
|
115978
|
+
return name.replace("/", "%2F");
|
|
115979
|
+
}
|
|
115980
|
+
return name;
|
|
115981
|
+
}
|
|
115982
|
+
function parseSemver(version) {
|
|
115983
|
+
const trimmed = version.trim().replace(/^v/, "");
|
|
115984
|
+
if (!trimmed) return null;
|
|
115985
|
+
const [core, prerelease] = trimmed.split("-", 2);
|
|
115986
|
+
if (!core) return null;
|
|
115987
|
+
const parts = core.split(".");
|
|
115988
|
+
if (parts.length < 3) return null;
|
|
115989
|
+
const major = Number.parseInt(parts[0] ?? "", 10);
|
|
115990
|
+
const minor = Number.parseInt(parts[1] ?? "", 10);
|
|
115991
|
+
const patch = Number.parseInt(parts[2] ?? "", 10);
|
|
115992
|
+
if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch)) {
|
|
115993
|
+
return null;
|
|
115994
|
+
}
|
|
115995
|
+
return { major, minor, patch, prerelease };
|
|
115996
|
+
}
|
|
115997
|
+
function compareSemver(a, b) {
|
|
115998
|
+
const parsedA = parseSemver(a);
|
|
115999
|
+
const parsedB = parseSemver(b);
|
|
116000
|
+
if (!parsedA || !parsedB) return 0;
|
|
116001
|
+
if (parsedA.major !== parsedB.major) {
|
|
116002
|
+
return parsedA.major > parsedB.major ? 1 : -1;
|
|
116003
|
+
}
|
|
116004
|
+
if (parsedA.minor !== parsedB.minor) {
|
|
116005
|
+
return parsedA.minor > parsedB.minor ? 1 : -1;
|
|
116006
|
+
}
|
|
116007
|
+
if (parsedA.patch !== parsedB.patch) {
|
|
116008
|
+
return parsedA.patch > parsedB.patch ? 1 : -1;
|
|
116009
|
+
}
|
|
116010
|
+
if (parsedA.prerelease && !parsedB.prerelease) return -1;
|
|
116011
|
+
if (!parsedA.prerelease && parsedB.prerelease) return 1;
|
|
116012
|
+
if (parsedA.prerelease && parsedB.prerelease) {
|
|
116013
|
+
if (parsedA.prerelease === parsedB.prerelease) return 0;
|
|
116014
|
+
return parsedA.prerelease > parsedB.prerelease ? 1 : -1;
|
|
116015
|
+
}
|
|
116016
|
+
return 0;
|
|
116017
|
+
}
|
|
116018
|
+
function resolvePackageManager(userAgent) {
|
|
116019
|
+
const ua = userAgent ?? process.env.npm_config_user_agent ?? "";
|
|
116020
|
+
if (ua.startsWith("bun")) return "bun";
|
|
116021
|
+
if (ua.includes("bun")) return "bun";
|
|
116022
|
+
if (ua.startsWith("npm")) return "npm";
|
|
116023
|
+
return "npm";
|
|
116024
|
+
}
|
|
116025
|
+
async function checkForUpdate(options) {
|
|
116026
|
+
const packageName = options.packageName ?? DEFAULT_PACKAGE;
|
|
116027
|
+
const fetchFn = options.fetchFn ?? fetch;
|
|
116028
|
+
const store2 = new AutoUpdateStore({
|
|
116029
|
+
configDir: options.configDir,
|
|
116030
|
+
now: options.now
|
|
116031
|
+
});
|
|
116032
|
+
const now = store2.getNow();
|
|
116033
|
+
const state = await store2.load();
|
|
116034
|
+
if (isWithinWindow(state.lastCheckAt, now, CHECK_THROTTLE_MS)) {
|
|
116035
|
+
const latest = state.lastKnownVersion;
|
|
116036
|
+
const updateAvailable2 = latest ? compareSemver(latest, options.currentVersion) > 0 : false;
|
|
116037
|
+
return { updateAvailable: updateAvailable2, latestVersion: latest, checked: false };
|
|
116038
|
+
}
|
|
116039
|
+
let latestVersion;
|
|
116040
|
+
try {
|
|
116041
|
+
const registryUrl = `${REGISTRY_BASE}${normalizePackageName(packageName)}`;
|
|
116042
|
+
const response = await fetchFn(registryUrl, {
|
|
116043
|
+
headers: { Accept: "application/json" }
|
|
116044
|
+
});
|
|
116045
|
+
if (response.ok) {
|
|
116046
|
+
const data2 = await response.json();
|
|
116047
|
+
latestVersion = data2["dist-tags"]?.latest;
|
|
116048
|
+
}
|
|
116049
|
+
} catch {
|
|
116050
|
+
}
|
|
116051
|
+
state.lastCheckAt = now.toISOString();
|
|
116052
|
+
if (latestVersion) {
|
|
116053
|
+
state.lastKnownVersion = latestVersion;
|
|
116054
|
+
}
|
|
116055
|
+
await store2.save(state);
|
|
116056
|
+
const updateAvailable = latestVersion !== void 0 && compareSemver(latestVersion, options.currentVersion) > 0;
|
|
116057
|
+
return { updateAvailable, latestVersion, checked: true };
|
|
116058
|
+
}
|
|
116059
|
+
async function performUpdate(options = {}) {
|
|
116060
|
+
const packageName = options.packageName ?? DEFAULT_PACKAGE;
|
|
116061
|
+
const store2 = new AutoUpdateStore({
|
|
116062
|
+
configDir: options.configDir,
|
|
116063
|
+
now: options.now
|
|
116064
|
+
});
|
|
116065
|
+
const now = store2.getNow();
|
|
116066
|
+
const packageManager = resolvePackageManager(options.userAgent);
|
|
116067
|
+
const spawnFn = options.spawnFn ?? spawn;
|
|
116068
|
+
const [command, args] = packageManager === "bun" ? ["bun", ["add", "-g", packageName]] : ["npm", ["install", "-g", packageName]];
|
|
116069
|
+
const exitCode = await new Promise((resolve7) => {
|
|
116070
|
+
try {
|
|
116071
|
+
const child = spawnFn(command, args, {
|
|
116072
|
+
stdio: "ignore",
|
|
116073
|
+
env: process.env
|
|
116074
|
+
});
|
|
116075
|
+
child.on("error", () => resolve7(1));
|
|
116076
|
+
child.on("close", (code) => resolve7(code ?? 1));
|
|
116077
|
+
} catch {
|
|
116078
|
+
resolve7(1);
|
|
116079
|
+
}
|
|
116080
|
+
});
|
|
116081
|
+
if (exitCode === 0) {
|
|
116082
|
+
const state = await store2.load();
|
|
116083
|
+
state.lastUpdateAt = now.toISOString();
|
|
116084
|
+
await store2.save(state);
|
|
116085
|
+
return true;
|
|
116086
|
+
}
|
|
116087
|
+
return false;
|
|
116088
|
+
}
|
|
116089
|
+
async function autoUpdateAfterCommand(options) {
|
|
116090
|
+
if (isAutoUpdateDisabled()) return;
|
|
116091
|
+
if (options.isDevMode) return;
|
|
116092
|
+
if (options.commandName === "mcp") return;
|
|
116093
|
+
if (options.format === "json") return;
|
|
116094
|
+
const store2 = new AutoUpdateStore({
|
|
116095
|
+
configDir: options.configDir,
|
|
116096
|
+
now: options.now
|
|
116097
|
+
});
|
|
116098
|
+
const now = store2.getNow();
|
|
116099
|
+
const state = await store2.load();
|
|
116100
|
+
if (isWithinWindow(state.lastUpdateAt, now, UPDATE_THROTTLE_MS)) return;
|
|
116101
|
+
const check = await (options.checkForUpdateFn ?? checkForUpdate)({
|
|
116102
|
+
currentVersion: options.currentVersion,
|
|
116103
|
+
packageName: options.packageName,
|
|
116104
|
+
configDir: options.configDir,
|
|
116105
|
+
now: options.now
|
|
116106
|
+
});
|
|
116107
|
+
if (!check.updateAvailable) return;
|
|
116108
|
+
state.lastUpdateAt = now.toISOString();
|
|
116109
|
+
await store2.save(state);
|
|
116110
|
+
const updated = await (options.performUpdateFn ?? performUpdate)({
|
|
116111
|
+
packageName: options.packageName,
|
|
116112
|
+
configDir: options.configDir,
|
|
116113
|
+
now: options.now
|
|
116114
|
+
});
|
|
116115
|
+
if (updated) return;
|
|
116116
|
+
}
|
|
116117
|
+
|
|
116118
|
+
// src/core/hint-engine.ts
|
|
116119
|
+
init_esm_shims();
|
|
116120
|
+
var DEFAULT_MAX_HINTS = 2;
|
|
116121
|
+
var getCommandCount = (state, command) => state.commands[command]?.count ?? 0;
|
|
116122
|
+
var hasCommand = (state, command) => getCommandCount(state, command) > 0;
|
|
116123
|
+
var hasCommandPrefix = (state, prefix) => Object.entries(state.commands).some(
|
|
116124
|
+
([name, entry]) => name.startsWith(prefix) && entry.count > 0
|
|
116125
|
+
);
|
|
116126
|
+
var hasMilestone = (state, milestone) => state.milestones[milestone]?.achieved ?? false;
|
|
116127
|
+
var shouldSuppressHints = (context) => context.quiet === true || context.format === "json";
|
|
116128
|
+
var resolveMaxHints = (context) => context.maxHints ?? DEFAULT_MAX_HINTS;
|
|
116129
|
+
var toHint = (rule) => ({
|
|
116130
|
+
id: rule.id,
|
|
116131
|
+
message: rule.message,
|
|
116132
|
+
audience: rule.audience
|
|
116133
|
+
});
|
|
116134
|
+
var DEFAULT_HINT_RULES = [
|
|
116135
|
+
{
|
|
116136
|
+
id: "onboarding.wizard",
|
|
116137
|
+
audience: "onboarding",
|
|
116138
|
+
message: "New here? Run `skill wizard` to set up your first product.",
|
|
116139
|
+
showWhen: (state) => state.totalRuns <= 2 && !hasCommand(state, "wizard"),
|
|
116140
|
+
retireWhen: (state) => hasCommand(state, "wizard") || hasMilestone(state, "wizard_completed")
|
|
116141
|
+
},
|
|
116142
|
+
{
|
|
116143
|
+
id: "onboarding.auth",
|
|
116144
|
+
audience: "onboarding",
|
|
116145
|
+
message: "Configure credentials with `skill init` to unlock the full CLI.",
|
|
116146
|
+
showWhen: (state) => state.totalRuns >= 1 && !hasMilestone(state, "auth_configured"),
|
|
116147
|
+
retireWhen: (state) => hasMilestone(state, "auth_configured")
|
|
116148
|
+
},
|
|
116149
|
+
{
|
|
116150
|
+
id: "discovery.health",
|
|
116151
|
+
audience: "discovery",
|
|
116152
|
+
message: "Check integrations fast with `skill health <app-slug>`.",
|
|
116153
|
+
showWhen: (state) => state.totalRuns >= 2 && !hasCommand(state, "health"),
|
|
116154
|
+
retireWhen: (state) => hasCommand(state, "health")
|
|
116155
|
+
},
|
|
116156
|
+
{
|
|
116157
|
+
id: "discovery.front.inbox",
|
|
116158
|
+
audience: "discovery",
|
|
116159
|
+
message: "List recent conversations via `skill front inbox <name-or-id>`.",
|
|
116160
|
+
showWhen: (state) => state.totalRuns >= 1 && !hasCommand(state, "front.inbox"),
|
|
116161
|
+
retireWhen: (state) => hasCommand(state, "front.inbox")
|
|
116162
|
+
},
|
|
116163
|
+
{
|
|
116164
|
+
id: "discovery.inngest",
|
|
116165
|
+
audience: "discovery",
|
|
116166
|
+
message: "Inspect workflows with `skill inngest stats --after 1d`.",
|
|
116167
|
+
showWhen: (state) => state.totalRuns >= 3 && !hasCommandPrefix(state, "inngest."),
|
|
116168
|
+
retireWhen: (state) => hasCommandPrefix(state, "inngest.")
|
|
116169
|
+
},
|
|
116170
|
+
{
|
|
116171
|
+
id: "discovery.axiom",
|
|
116172
|
+
audience: "discovery",
|
|
116173
|
+
message: 'Query logs quickly with `skill axiom query "<APL>" --since 24h`.',
|
|
116174
|
+
showWhen: (state) => state.totalRuns >= 3 && !hasCommandPrefix(state, "axiom."),
|
|
116175
|
+
retireWhen: (state) => hasCommandPrefix(state, "axiom.")
|
|
116176
|
+
},
|
|
116177
|
+
{
|
|
116178
|
+
id: "context.front.triage",
|
|
116179
|
+
audience: "contextual",
|
|
116180
|
+
postRun: true,
|
|
116181
|
+
message: "Tip: `skill front triage <inbox-id>` surfaces unassigned threads.",
|
|
116182
|
+
showWhen: (state, context) => context.command === "front.inbox" && !hasCommand(state, "front.triage"),
|
|
116183
|
+
retireWhen: (state) => hasCommand(state, "front.triage")
|
|
116184
|
+
},
|
|
116185
|
+
{
|
|
116186
|
+
id: "context.front.conversation",
|
|
116187
|
+
audience: "contextual",
|
|
116188
|
+
postRun: true,
|
|
116189
|
+
message: "Tip: `skill front conversation <id> -m` shows the full thread.",
|
|
116190
|
+
showWhen: (state, context) => context.command === "front.message" && !hasCommand(state, "front.conversation"),
|
|
116191
|
+
retireWhen: (state) => hasCommand(state, "front.conversation")
|
|
116192
|
+
},
|
|
116193
|
+
{
|
|
116194
|
+
id: "context.inngest.run",
|
|
116195
|
+
audience: "contextual",
|
|
116196
|
+
postRun: true,
|
|
116197
|
+
message: "Tip: drill in with `skill inngest run <id>` or `skill inngest trace <run-id>`.",
|
|
116198
|
+
showWhen: (state, context) => context.command === "inngest.failures" && !hasCommand(state, "inngest.run") && !hasCommand(state, "inngest.trace"),
|
|
116199
|
+
retireWhen: (state) => hasCommand(state, "inngest.run") || hasCommand(state, "inngest.trace")
|
|
116200
|
+
}
|
|
116201
|
+
];
|
|
116202
|
+
var HintEngine = class {
|
|
116203
|
+
rules;
|
|
116204
|
+
constructor(rules = DEFAULT_HINT_RULES) {
|
|
116205
|
+
this.rules = rules;
|
|
116206
|
+
}
|
|
116207
|
+
getHints(state, context) {
|
|
116208
|
+
if (shouldSuppressHints(context)) return [];
|
|
116209
|
+
const maxHints = resolveMaxHints(context);
|
|
116210
|
+
if (maxHints <= 0) return [];
|
|
116211
|
+
const hints = [];
|
|
116212
|
+
for (const rule of this.rules) {
|
|
116213
|
+
if (rule.postRun) continue;
|
|
116214
|
+
if (!rule.showWhen(state, context)) continue;
|
|
116215
|
+
if (rule.retireWhen(state, context)) continue;
|
|
116216
|
+
hints.push(toHint(rule));
|
|
116217
|
+
if (hints.length >= maxHints) break;
|
|
116218
|
+
}
|
|
116219
|
+
return hints;
|
|
116220
|
+
}
|
|
116221
|
+
getPostRunHint(state, context) {
|
|
116222
|
+
if (shouldSuppressHints(context)) return null;
|
|
116223
|
+
const maxHints = resolveMaxHints(context);
|
|
116224
|
+
if (maxHints <= 0) return null;
|
|
116225
|
+
const previouslyShown = context.previouslyShown ?? 0;
|
|
116226
|
+
if (previouslyShown >= maxHints) return null;
|
|
116227
|
+
for (const rule of this.rules) {
|
|
116228
|
+
if (!rule.postRun) continue;
|
|
116229
|
+
if (!rule.showWhen(state, context)) continue;
|
|
116230
|
+
if (rule.retireWhen(state, context)) continue;
|
|
116231
|
+
return toHint(rule);
|
|
116232
|
+
}
|
|
116233
|
+
return null;
|
|
116234
|
+
}
|
|
116235
|
+
};
|
|
116236
|
+
var writeHints = (hints, stderr) => {
|
|
116237
|
+
for (const hint of hints) {
|
|
116238
|
+
stderr.write(`${hint.message}
|
|
116239
|
+
`);
|
|
116240
|
+
}
|
|
116241
|
+
};
|
|
116242
|
+
|
|
116243
|
+
// src/core/telemetry.ts
|
|
116244
|
+
init_esm_shims();
|
|
116245
|
+
import { Axiom as Axiom2 } from "@axiomhq/js";
|
|
116246
|
+
var DEFAULT_DATASET = "support-agent";
|
|
116247
|
+
var axiomClient = null;
|
|
116248
|
+
function isTelemetryDisabled() {
|
|
116249
|
+
return process.env.SKILL_NO_TELEMETRY === "1";
|
|
116250
|
+
}
|
|
116251
|
+
function getAxiomClient2() {
|
|
116252
|
+
const token = process.env.AXIOM_TOKEN;
|
|
116253
|
+
if (!token) return null;
|
|
116254
|
+
if (!axiomClient) {
|
|
116255
|
+
axiomClient = new Axiom2({ token });
|
|
116256
|
+
}
|
|
116257
|
+
return axiomClient;
|
|
116258
|
+
}
|
|
116259
|
+
function resolveTelemetryUser() {
|
|
116260
|
+
const raw = process.env.USER ?? process.env.LOGNAME ?? process.env.USERNAME ?? "";
|
|
116261
|
+
const trimmed = raw.trim();
|
|
116262
|
+
if (!trimmed) return void 0;
|
|
116263
|
+
const base = trimmed.split("@")[0]?.trim();
|
|
116264
|
+
return base || void 0;
|
|
116265
|
+
}
|
|
116266
|
+
async function sendTelemetryEvent(event) {
|
|
116267
|
+
if (isTelemetryDisabled()) return;
|
|
116268
|
+
const client = getAxiomClient2();
|
|
116269
|
+
if (!client) return;
|
|
116270
|
+
const dataset = process.env.AXIOM_DATASET || DEFAULT_DATASET;
|
|
116271
|
+
const safeEvent = {
|
|
116272
|
+
command: event.command,
|
|
116273
|
+
duration: event.duration,
|
|
116274
|
+
success: event.success,
|
|
116275
|
+
platform: event.platform,
|
|
116276
|
+
user: event.user
|
|
116277
|
+
};
|
|
116278
|
+
try {
|
|
116279
|
+
await client.ingest(dataset, {
|
|
116280
|
+
_time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
116281
|
+
...safeEvent
|
|
116282
|
+
});
|
|
116283
|
+
} catch {
|
|
116284
|
+
}
|
|
116285
|
+
}
|
|
116286
|
+
|
|
116287
|
+
// src/core/usage-tracker.ts
|
|
116288
|
+
init_esm_shims();
|
|
116289
|
+
import { writeFile as writeFile8 } from "fs/promises";
|
|
116290
|
+
import { homedir as homedir3 } from "os";
|
|
116291
|
+
import { dirname as dirname5, join as join13 } from "path";
|
|
116292
|
+
var CONFIG_DIR_NAME2 = "skill-cli";
|
|
116293
|
+
var USAGE_FILE_NAME = "usage.json";
|
|
116294
|
+
function resolveConfigDir2(configDir) {
|
|
116295
|
+
if (configDir) return configDir;
|
|
116296
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
116297
|
+
if (xdgConfigHome && xdgConfigHome.trim() !== "") {
|
|
116298
|
+
return join13(xdgConfigHome, CONFIG_DIR_NAME2);
|
|
116299
|
+
}
|
|
116300
|
+
return join13(homedir3(), ".config", CONFIG_DIR_NAME2);
|
|
116301
|
+
}
|
|
116302
|
+
function createDefaultState(now) {
|
|
116303
|
+
return {
|
|
116304
|
+
firstRun: now.toISOString(),
|
|
116305
|
+
totalRuns: 0,
|
|
116306
|
+
commands: {},
|
|
116307
|
+
milestones: {}
|
|
116308
|
+
};
|
|
116309
|
+
}
|
|
116310
|
+
function isUsageState(value) {
|
|
116311
|
+
if (!value || typeof value !== "object") return false;
|
|
116312
|
+
const state = value;
|
|
116313
|
+
if (typeof state.firstRun !== "string") return false;
|
|
116314
|
+
if (typeof state.totalRuns !== "number") return false;
|
|
116315
|
+
if (!state.commands || typeof state.commands !== "object") return false;
|
|
116316
|
+
if (!state.milestones || typeof state.milestones !== "object") return false;
|
|
116317
|
+
for (const entry of Object.values(state.commands)) {
|
|
116318
|
+
if (!entry || typeof entry !== "object") return false;
|
|
116319
|
+
if (typeof entry.count !== "number") return false;
|
|
116320
|
+
if (typeof entry.firstRun !== "string") return false;
|
|
116321
|
+
if (typeof entry.lastRun !== "string") return false;
|
|
116322
|
+
}
|
|
116323
|
+
for (const entry of Object.values(state.milestones)) {
|
|
116324
|
+
if (!entry || typeof entry !== "object") return false;
|
|
116325
|
+
if (typeof entry.achieved !== "boolean") return false;
|
|
116326
|
+
if (entry.achievedAt !== void 0 && typeof entry.achievedAt !== "string") {
|
|
116327
|
+
return false;
|
|
116328
|
+
}
|
|
116329
|
+
}
|
|
116330
|
+
return true;
|
|
116331
|
+
}
|
|
116332
|
+
var UsageTracker = class {
|
|
116333
|
+
filePath;
|
|
116334
|
+
now;
|
|
116335
|
+
statePromise;
|
|
116336
|
+
constructor(options = {}) {
|
|
116337
|
+
const configDir = resolveConfigDir2(options.configDir);
|
|
116338
|
+
this.filePath = join13(configDir, USAGE_FILE_NAME);
|
|
116339
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
116340
|
+
}
|
|
116341
|
+
async loadState() {
|
|
116342
|
+
if (!this.statePromise) {
|
|
116343
|
+
this.statePromise = this.loadStateInternal();
|
|
116344
|
+
}
|
|
116345
|
+
return this.statePromise;
|
|
116346
|
+
}
|
|
116347
|
+
async loadStateInternal() {
|
|
116348
|
+
try {
|
|
116349
|
+
if (await pathExists(this.filePath)) {
|
|
116350
|
+
const data2 = await readJson(this.filePath);
|
|
116351
|
+
if (isUsageState(data2)) {
|
|
116352
|
+
return data2;
|
|
116353
|
+
}
|
|
116354
|
+
}
|
|
116355
|
+
} catch {
|
|
116356
|
+
}
|
|
116357
|
+
return createDefaultState(this.now());
|
|
116358
|
+
}
|
|
116359
|
+
async saveState(state) {
|
|
116360
|
+
try {
|
|
116361
|
+
await ensureDir(dirname5(this.filePath));
|
|
116362
|
+
await writeFile8(this.filePath, JSON.stringify(state, null, 2), "utf-8");
|
|
116363
|
+
} catch {
|
|
116364
|
+
}
|
|
116365
|
+
}
|
|
116366
|
+
async record(command, _opts = {}) {
|
|
116367
|
+
const state = await this.loadState();
|
|
116368
|
+
const nowIso = this.now().toISOString();
|
|
116369
|
+
state.totalRuns += 1;
|
|
116370
|
+
const existing = state.commands[command];
|
|
116371
|
+
if (existing) {
|
|
116372
|
+
existing.count += 1;
|
|
116373
|
+
existing.lastRun = nowIso;
|
|
116374
|
+
} else {
|
|
116375
|
+
state.commands[command] = {
|
|
116376
|
+
count: 1,
|
|
116377
|
+
firstRun: nowIso,
|
|
116378
|
+
lastRun: nowIso
|
|
116379
|
+
};
|
|
116380
|
+
}
|
|
116381
|
+
await this.saveState(state);
|
|
116382
|
+
return state;
|
|
116383
|
+
}
|
|
116384
|
+
async getUsage() {
|
|
116385
|
+
return this.loadState();
|
|
116386
|
+
}
|
|
116387
|
+
async getCommandCount(command) {
|
|
116388
|
+
const state = await this.loadState();
|
|
116389
|
+
return state.commands[command]?.count ?? 0;
|
|
116390
|
+
}
|
|
116391
|
+
async hasMilestone(name) {
|
|
116392
|
+
const state = await this.loadState();
|
|
116393
|
+
return state.milestones[name]?.achieved ?? false;
|
|
116394
|
+
}
|
|
116395
|
+
async setMilestone(name) {
|
|
116396
|
+
const state = await this.loadState();
|
|
116397
|
+
const existing = state.milestones[name];
|
|
116398
|
+
if (!existing || !existing.achieved) {
|
|
116399
|
+
state.milestones[name] = {
|
|
116400
|
+
achieved: true,
|
|
116401
|
+
achievedAt: this.now().toISOString()
|
|
116402
|
+
};
|
|
116403
|
+
await this.saveState(state);
|
|
116404
|
+
}
|
|
116405
|
+
}
|
|
116406
|
+
async totalRuns() {
|
|
116407
|
+
const state = await this.loadState();
|
|
116408
|
+
return state.totalRuns;
|
|
116409
|
+
}
|
|
116410
|
+
async daysSinceFirstRun() {
|
|
116411
|
+
const state = await this.loadState();
|
|
116412
|
+
const firstRunMs = Date.parse(state.firstRun);
|
|
116413
|
+
if (Number.isNaN(firstRunMs)) return 0;
|
|
116414
|
+
const diffMs = this.now().getTime() - firstRunMs;
|
|
116415
|
+
if (diffMs <= 0) return 0;
|
|
116416
|
+
return Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
116417
|
+
}
|
|
116418
|
+
};
|
|
116419
|
+
var cachedUsageTracker;
|
|
116420
|
+
function getUsageTracker() {
|
|
116421
|
+
if (!cachedUsageTracker) {
|
|
116422
|
+
cachedUsageTracker = new UsageTracker();
|
|
116423
|
+
}
|
|
116424
|
+
return cachedUsageTracker;
|
|
116425
|
+
}
|
|
116426
|
+
|
|
115847
116427
|
// src/mcp/server.ts
|
|
115848
116428
|
init_esm_shims();
|
|
115849
116429
|
import { createInterface as createInterface2 } from "readline";
|
|
@@ -116348,13 +116928,64 @@ var runtimeTarget = `bun-${process.platform}-${process.arch}`;
|
|
|
116348
116928
|
var buildVersion = typeof BUILD_VERSION !== "undefined" && BUILD_VERSION.length > 0 ? BUILD_VERSION : "0.0.0-dev";
|
|
116349
116929
|
var buildCommit = typeof BUILD_COMMIT !== "undefined" && BUILD_COMMIT.length > 0 ? BUILD_COMMIT : "dev";
|
|
116350
116930
|
var buildTarget = typeof BUILD_TARGET !== "undefined" && BUILD_TARGET.length > 0 ? BUILD_TARGET : runtimeTarget;
|
|
116931
|
+
var isDevBuild = buildVersion.includes("dev") || buildCommit === "dev";
|
|
116351
116932
|
var versionLabel = `skill v${buildVersion} (${buildCommit}) ${buildTarget}`;
|
|
116352
116933
|
var program2 = new Command4();
|
|
116353
|
-
|
|
116934
|
+
var hintEngine = new HintEngine();
|
|
116935
|
+
var usageTracker = getUsageTracker();
|
|
116936
|
+
var usageState = await (async () => {
|
|
116937
|
+
try {
|
|
116938
|
+
return await usageTracker.getUsage();
|
|
116939
|
+
} catch {
|
|
116940
|
+
return null;
|
|
116941
|
+
}
|
|
116942
|
+
})();
|
|
116943
|
+
var hintCounts = /* @__PURE__ */ new WeakMap();
|
|
116944
|
+
var commandStartTimes = /* @__PURE__ */ new WeakMap();
|
|
116945
|
+
var resolveCommandName = (command) => {
|
|
116946
|
+
const names = [];
|
|
116947
|
+
let current = command;
|
|
116948
|
+
while (current) {
|
|
116949
|
+
const name = current.name();
|
|
116950
|
+
if (name) names.unshift(name);
|
|
116951
|
+
current = current.parent;
|
|
116952
|
+
}
|
|
116953
|
+
if (names[0] === "skill") names.shift();
|
|
116954
|
+
return names.join(".");
|
|
116955
|
+
};
|
|
116956
|
+
var resolveHintContext = (command) => {
|
|
116957
|
+
const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
|
|
116958
|
+
...command.parent?.opts(),
|
|
116959
|
+
...command.opts()
|
|
116960
|
+
};
|
|
116961
|
+
const outputJson = opts.json === true || opts.format === "json";
|
|
116962
|
+
const suppressForPipe = process.env.SKILL_CLI_FORCE_HINTS === "1" ? false : process.stdout.isTTY !== true;
|
|
116963
|
+
return {
|
|
116964
|
+
command: resolveCommandName(command),
|
|
116965
|
+
format: outputJson ? "json" : opts.format,
|
|
116966
|
+
quiet: opts.quiet === true || suppressForPipe
|
|
116967
|
+
};
|
|
116968
|
+
};
|
|
116969
|
+
var resolveMilestones = (commandName) => {
|
|
116970
|
+
switch (commandName) {
|
|
116971
|
+
case "wizard":
|
|
116972
|
+
return ["wizard_completed"];
|
|
116973
|
+
case "auth.setup":
|
|
116974
|
+
case "init":
|
|
116975
|
+
return ["auth_configured"];
|
|
116976
|
+
default:
|
|
116977
|
+
return [];
|
|
116978
|
+
}
|
|
116979
|
+
};
|
|
116980
|
+
program2.name("skill").description(getRootAdaptiveDescription(usageState)).version(versionLabel).option("-f, --format <format>", "Output format (json|text|table)").option("-v, --verbose", "Enable verbose output").option("-q, --quiet", "Suppress non-error output").option(
|
|
116354
116981
|
"--rate-limit <n>",
|
|
116355
116982
|
"Override Front API rate limit per minute",
|
|
116356
116983
|
(v) => Number.parseInt(v, 10)
|
|
116357
116984
|
);
|
|
116985
|
+
program2.addHelpText(
|
|
116986
|
+
"after",
|
|
116987
|
+
"\n Need help? Start with:\n skill auth setup Set up credentials (1Password)\n skill front inbox See what needs attention\n skill --help This message\n"
|
|
116988
|
+
);
|
|
116358
116989
|
program2.hook("preAction", (thisCommand, actionCommand) => {
|
|
116359
116990
|
const opts = typeof actionCommand.optsWithGlobals === "function" ? actionCommand.optsWithGlobals() : thisCommand.opts();
|
|
116360
116991
|
const rateLimit = opts.rateLimit;
|
|
@@ -116362,18 +116993,105 @@ program2.hook("preAction", (thisCommand, actionCommand) => {
|
|
|
116362
116993
|
process.env.SKILL_RATE_LIMIT = String(rateLimit);
|
|
116363
116994
|
}
|
|
116364
116995
|
});
|
|
116996
|
+
program2.hook("preAction", (_thisCommand, actionCommand) => {
|
|
116997
|
+
commandStartTimes.set(actionCommand, Date.now());
|
|
116998
|
+
});
|
|
116999
|
+
program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
117000
|
+
try {
|
|
117001
|
+
const context = resolveHintContext(actionCommand);
|
|
117002
|
+
const state = await usageTracker.getUsage();
|
|
117003
|
+
const hints = hintEngine.getHints(state, context);
|
|
117004
|
+
writeHints(hints, process.stderr);
|
|
117005
|
+
hintCounts.set(actionCommand, hints.length);
|
|
117006
|
+
} catch {
|
|
117007
|
+
}
|
|
117008
|
+
});
|
|
117009
|
+
program2.hook("postAction", async (_thisCommand, actionCommand) => {
|
|
117010
|
+
try {
|
|
117011
|
+
const context = resolveHintContext(actionCommand);
|
|
117012
|
+
const state = await usageTracker.record(context.command);
|
|
117013
|
+
const milestones = resolveMilestones(context.command);
|
|
117014
|
+
for (const milestone of milestones) {
|
|
117015
|
+
await usageTracker.setMilestone(milestone);
|
|
117016
|
+
}
|
|
117017
|
+
const previouslyShown = hintCounts.get(actionCommand) ?? 0;
|
|
117018
|
+
const postHint = hintEngine.getPostRunHint(state, {
|
|
117019
|
+
...context,
|
|
117020
|
+
previouslyShown
|
|
117021
|
+
});
|
|
117022
|
+
if (postHint) writeHints([postHint], process.stderr);
|
|
117023
|
+
} catch {
|
|
117024
|
+
}
|
|
117025
|
+
try {
|
|
117026
|
+
const startTime = commandStartTimes.get(actionCommand) ?? Date.now();
|
|
117027
|
+
const duration = Math.max(0, Date.now() - startTime);
|
|
117028
|
+
const exitCode = process.exitCode ?? 0;
|
|
117029
|
+
const commandName = resolveCommandName(actionCommand);
|
|
117030
|
+
void sendTelemetryEvent({
|
|
117031
|
+
command: commandName,
|
|
117032
|
+
duration,
|
|
117033
|
+
success: exitCode === 0,
|
|
117034
|
+
platform: process.platform,
|
|
117035
|
+
user: resolveTelemetryUser()
|
|
117036
|
+
});
|
|
117037
|
+
} catch {
|
|
117038
|
+
}
|
|
117039
|
+
try {
|
|
117040
|
+
const context = resolveHintContext(actionCommand);
|
|
117041
|
+
await autoUpdateAfterCommand({
|
|
117042
|
+
commandName: context.command,
|
|
117043
|
+
currentVersion: buildVersion,
|
|
117044
|
+
format: context.format,
|
|
117045
|
+
isDevMode: isDevBuild
|
|
117046
|
+
});
|
|
117047
|
+
} catch {
|
|
117048
|
+
}
|
|
117049
|
+
});
|
|
116365
117050
|
program2.command("init").description("Initialize a new app integration (quick mode)").argument(
|
|
116366
117051
|
"[name]",
|
|
116367
117052
|
"Name of the integration (required in non-interactive mode)"
|
|
116368
|
-
).option("--json", "Output result as JSON (machine-readable)").action(
|
|
116369
|
-
|
|
117053
|
+
).option("--json", "Output result as JSON (machine-readable)").action(async (name, options, command) => {
|
|
117054
|
+
const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
|
|
117055
|
+
...command.parent?.opts(),
|
|
117056
|
+
...command.opts()
|
|
117057
|
+
};
|
|
117058
|
+
const ctx = await createContext({
|
|
117059
|
+
format: options.json ? "json" : opts.format,
|
|
117060
|
+
verbose: opts.verbose,
|
|
117061
|
+
quiet: opts.quiet
|
|
117062
|
+
});
|
|
117063
|
+
await init2(ctx, name, options);
|
|
117064
|
+
});
|
|
117065
|
+
program2.command("wizard").description("Interactive wizard for setting up a new property").option("--json", "Output result as JSON (machine-readable)").action(async (options, command) => {
|
|
117066
|
+
const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
|
|
117067
|
+
...command.parent?.opts(),
|
|
117068
|
+
...command.opts()
|
|
117069
|
+
};
|
|
117070
|
+
const ctx = await createContext({
|
|
117071
|
+
format: options.json ? "json" : opts.format,
|
|
117072
|
+
verbose: opts.verbose,
|
|
117073
|
+
quiet: opts.quiet
|
|
117074
|
+
});
|
|
117075
|
+
await wizard(ctx, options);
|
|
117076
|
+
});
|
|
116370
117077
|
program2.command("health").description("Test integration endpoint health").argument(
|
|
116371
117078
|
"[slug|url]",
|
|
116372
117079
|
"App slug (from database) or URL (e.g., https://totaltypescript.com)"
|
|
116373
117080
|
).option(
|
|
116374
117081
|
"-s, --secret <secret>",
|
|
116375
117082
|
"Webhook secret (required for direct URL mode)"
|
|
116376
|
-
).option("-l, --list", "List all registered apps").option("--ids-only", "Output only IDs (one per line)").option("--json", "Output result as JSON (machine-readable)").action(
|
|
117083
|
+
).option("-l, --list", "List all registered apps").option("--ids-only", "Output only IDs (one per line)").option("--json", "Output result as JSON (machine-readable)").action(async (slugOrUrl, options, command) => {
|
|
117084
|
+
const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
|
|
117085
|
+
...command.parent?.opts(),
|
|
117086
|
+
...command.opts()
|
|
117087
|
+
};
|
|
117088
|
+
const ctx = await createContext({
|
|
117089
|
+
format: options.json ? "json" : opts.format,
|
|
117090
|
+
verbose: opts.verbose,
|
|
117091
|
+
quiet: opts.quiet
|
|
117092
|
+
});
|
|
117093
|
+
await health2(ctx, slugOrUrl, options);
|
|
117094
|
+
});
|
|
116377
117095
|
program2.command("eval").description("Run evals against a dataset").argument("<type>", "Eval type (e.g., routing)").argument("<dataset>", "Path to dataset JSON file").option("--json", "Output result as JSON (machine-readable)").option(
|
|
116378
117096
|
"--min-precision <number>",
|
|
116379
117097
|
"Minimum precision threshold (default: 0.92)",
|
|
@@ -116410,8 +117128,8 @@ program2.command("eval").description("Run evals against a dataset").argument("<t
|
|
|
116410
117128
|
});
|
|
116411
117129
|
});
|
|
116412
117130
|
registerDbStatusCommand(program2);
|
|
116413
|
-
registerFrontCommands(program2);
|
|
116414
|
-
registerInngestCommands(program2);
|
|
117131
|
+
registerFrontCommands(program2, usageState);
|
|
117132
|
+
registerInngestCommands(program2, usageState);
|
|
116415
117133
|
registerAxiomCommands(program2);
|
|
116416
117134
|
registerEvalLocalCommands(program2);
|
|
116417
117135
|
registerEvalPipelineCommands(program2);
|
|
@@ -116424,9 +117142,11 @@ registerMemoryCommands(program2);
|
|
|
116424
117142
|
registerFaqCommands(program2);
|
|
116425
117143
|
registerDeployCommands(program2);
|
|
116426
117144
|
registerKbCommands(program2);
|
|
116427
|
-
registerAuthCommands(program2);
|
|
117145
|
+
registerAuthCommands(program2, usageState);
|
|
116428
117146
|
registerPluginSyncCommand(program2);
|
|
116429
|
-
program2.command("mcp").description(
|
|
117147
|
+
program2.command("mcp").description(
|
|
117148
|
+
"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)"
|
|
117149
|
+
).action(async () => {
|
|
116430
117150
|
const server = createMcpServer();
|
|
116431
117151
|
await server.start();
|
|
116432
117152
|
});
|