@skillrecordings/cli 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +660 -159
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -56,8 +56,10 @@ import "./chunk-F4EM72IH.js";
|
|
|
56
56
|
import {
|
|
57
57
|
addShellIntegration,
|
|
58
58
|
autoBootstrapKeychain,
|
|
59
|
+
getFromKeychain,
|
|
59
60
|
getKeychainStatus,
|
|
60
61
|
isKeychainSupported,
|
|
62
|
+
isOpCliAvailable,
|
|
61
63
|
storeInKeychain
|
|
62
64
|
} from "./chunk-7SQU7KCI.js";
|
|
63
65
|
import {
|
|
@@ -74618,9 +74620,9 @@ function parseEnvContent(content) {
|
|
|
74618
74620
|
return env;
|
|
74619
74621
|
}
|
|
74620
74622
|
async function decryptEnvFile(encryptedPath) {
|
|
74621
|
-
const { existsSync:
|
|
74623
|
+
const { existsSync: existsSync17 } = await import("fs");
|
|
74622
74624
|
const { readFile: readFile10 } = await import("fs/promises");
|
|
74623
|
-
if (!
|
|
74625
|
+
if (!existsSync17(encryptedPath)) {
|
|
74624
74626
|
return {};
|
|
74625
74627
|
}
|
|
74626
74628
|
const ageKey = await getAgeKeyFrom1Password();
|
|
@@ -74641,8 +74643,8 @@ async function getAgeKeyFrom1Password() {
|
|
|
74641
74643
|
return process.env.SKILL_AGE_KEY;
|
|
74642
74644
|
}
|
|
74643
74645
|
try {
|
|
74644
|
-
const { getFromKeychain, storeInKeychain: storeInKeychain2, autoBootstrapKeychain: autoBootstrapKeychain2 } = await import("./keychain-IEZHT5WN.js");
|
|
74645
|
-
const fromKeychain =
|
|
74646
|
+
const { getFromKeychain: getFromKeychain2, storeInKeychain: storeInKeychain2, autoBootstrapKeychain: autoBootstrapKeychain2 } = await import("./keychain-IEZHT5WN.js");
|
|
74647
|
+
const fromKeychain = getFromKeychain2("age-private-key");
|
|
74646
74648
|
if (fromKeychain) return fromKeychain;
|
|
74647
74649
|
let opToken = process.env.OP_SERVICE_ACCOUNT_TOKEN;
|
|
74648
74650
|
if (!opToken) {
|
|
@@ -74750,7 +74752,7 @@ var ROOT_DESCRIPTIONS = {
|
|
|
74750
74752
|
minimal: "Skill Recordings support agent CLI. Try: skill wizard, skill keys, skill front inbox, skill front triage. Use --help for details."
|
|
74751
74753
|
};
|
|
74752
74754
|
var FRONT_DESCRIPTIONS = {
|
|
74753
|
-
full: "Front conversations, inboxes, tags, archival, and reporting.\n\n Start here:\n skill front inbox See unassigned conversations\n skill front inbox support List conversations in a specific inbox\n skill front triage AI-powered categorization of inbox items\n\n Investigate a conversation:\n skill front conversation <id> -m Full conversation with messages\n skill front message <id> Single message details + body\n\n Take action:\n skill front assign <id> Assign to a teammate\n skill front reply <id> Draft a reply (HITL, never auto-sends)\n skill front tag <id> Add a tag\n skill front archive <id> Archive a resolved conversation\n\n Bulk operations:\n skill front bulk-archive Archive old/spam conversations\n skill front report Volume + tag + sender forensics\n\n All commands accept --json for HATEOAS-enriched output with _links and _actions.",
|
|
74755
|
+
full: "Front conversations, inboxes, tags, archival, and reporting.\n\n Start here:\n skill front inbox See unassigned conversations\n skill front inbox support List conversations in a specific inbox\n skill front triage AI-powered categorization of inbox items\n\n Investigate a conversation:\n skill front conversation <id> -m Full conversation with messages\n skill front message <id> Single message details + body\n\n Take action:\n skill front assign <id> Assign to a teammate\n skill front reply <id> Draft a reply (HITL, never auto-sends)\n skill front tag <id> Add a tag\n skill front archive <id> Archive a resolved conversation\n\n Bulk operations:\n skill front bulk-archive Archive old/spam conversations\n skill front report Volume + tag + sender forensics\n\n All commands accept --json for HATEOAS-enriched output with _links and _actions.\n\n Environment: FRONT_API_TOKEN required. Run `skill doctor` to check.",
|
|
74754
74756
|
abbreviated: "Front API workflows for inbox triage and conversation actions.\n\n Common:\n skill front inbox\n skill front triage\n skill front conversation <id> -m\n skill front reply <id>\n skill front archive <id>",
|
|
74755
74757
|
minimal: "Front API commands (inbox, triage, assign, reply, archive)."
|
|
74756
74758
|
};
|
|
@@ -74760,7 +74762,7 @@ var AUTH_DESCRIPTIONS = {
|
|
|
74760
74762
|
minimal: "Auth status commands (auth status, auth whoami)."
|
|
74761
74763
|
};
|
|
74762
74764
|
var INNGEST_DESCRIPTIONS = {
|
|
74763
|
-
full: "Inngest event and workflow commands.\n\n Debug pipeline runs:\n skill inngest runs --status failed --after 1h Recent failures\n skill inngest events --after 12h Recent events\n skill inngest investigate <run-id> Deep-dive a specific run",
|
|
74765
|
+
full: "Inngest event and workflow commands.\n\n Debug pipeline runs:\n skill inngest runs --status failed --after 1h Recent failures\n skill inngest events --after 12h Recent events\n skill inngest investigate <run-id> Deep-dive a specific run\n\n Environment: INNGEST_SIGNING_KEY required. Run `skill doctor` to check.",
|
|
74764
74766
|
abbreviated: "Inngest events and workflow runs.\n\n Common:\n skill inngest runs --status failed --after 1h\n skill inngest events --after 12h\n skill inngest investigate <run-id>",
|
|
74765
74767
|
minimal: "Inngest events and runs debugging."
|
|
74766
74768
|
};
|
|
@@ -76836,7 +76838,9 @@ async function listApprovals(ctx, options) {
|
|
|
76836
76838
|
}
|
|
76837
76839
|
}
|
|
76838
76840
|
function registerAxiomCommands(program3) {
|
|
76839
|
-
const axiom = program3.command("axiom").description(
|
|
76841
|
+
const axiom = program3.command("axiom").description(
|
|
76842
|
+
"Query Axiom logs and traces.\n Environment: AXIOM_TOKEN required. Run `skill doctor` to check."
|
|
76843
|
+
);
|
|
76840
76844
|
axiom.command("query").description("Run a raw APL query").argument("<apl>", "APL query string").option("-s, --since <time>", "Time range (e.g., 1h, 24h, 7d)", "24h").option("--json", "Output as JSON").action(async (apl, options, command) => {
|
|
76841
76845
|
const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
|
|
76842
76846
|
...command.parent?.opts(),
|
|
@@ -77074,11 +77078,11 @@ Saved to ${options.output}`);
|
|
|
77074
77078
|
}
|
|
77075
77079
|
}
|
|
77076
77080
|
async function toEvalite(options) {
|
|
77077
|
-
const { readFileSync:
|
|
77081
|
+
const { readFileSync: readFileSync14 } = await import("fs");
|
|
77078
77082
|
const { ctx } = options;
|
|
77079
77083
|
const outputJson = ctx.format === "json";
|
|
77080
77084
|
const data2 = JSON.parse(
|
|
77081
|
-
|
|
77085
|
+
readFileSync14(options.input, "utf-8")
|
|
77082
77086
|
);
|
|
77083
77087
|
const evaliteData = data2.map((d) => ({
|
|
77084
77088
|
input: d.triggerMessage.body,
|
|
@@ -77882,6 +77886,170 @@ function registerDeployCommands(program3) {
|
|
|
77882
77886
|
});
|
|
77883
77887
|
}
|
|
77884
77888
|
|
|
77889
|
+
// src/commands/doctor.ts
|
|
77890
|
+
init_esm_shims();
|
|
77891
|
+
import { execSync as execSync3 } from "child_process";
|
|
77892
|
+
import { existsSync as existsSync6 } from "fs";
|
|
77893
|
+
import { join as join3 } from "path";
|
|
77894
|
+
var REQUIRED_ENV_VARS = [
|
|
77895
|
+
"DATABASE_URL",
|
|
77896
|
+
"INNGEST_SIGNING_KEY",
|
|
77897
|
+
"INNGEST_EVENT_KEY",
|
|
77898
|
+
"FRONT_API_TOKEN",
|
|
77899
|
+
"LINEAR_API_KEY",
|
|
77900
|
+
"AXIOM_TOKEN",
|
|
77901
|
+
"SLACK_BOT_TOKEN",
|
|
77902
|
+
"UPSTASH_REDIS_REST_URL",
|
|
77903
|
+
"UPSTASH_VECTOR_REST_URL"
|
|
77904
|
+
];
|
|
77905
|
+
function checkEnvVars() {
|
|
77906
|
+
return REQUIRED_ENV_VARS.map((varName) => {
|
|
77907
|
+
const value = process.env[varName];
|
|
77908
|
+
return {
|
|
77909
|
+
name: varName,
|
|
77910
|
+
status: value ? "ok" : "warn",
|
|
77911
|
+
message: value ? void 0 : "Not set"
|
|
77912
|
+
};
|
|
77913
|
+
});
|
|
77914
|
+
}
|
|
77915
|
+
function checkKeychain() {
|
|
77916
|
+
const checks = [];
|
|
77917
|
+
const opAvailable = isOpCliAvailable();
|
|
77918
|
+
checks.push({
|
|
77919
|
+
name: "op CLI",
|
|
77920
|
+
status: opAvailable ? "ok" : "warn",
|
|
77921
|
+
message: opAvailable ? void 0 : "Not installed or not authenticated"
|
|
77922
|
+
});
|
|
77923
|
+
const opToken = getFromKeychain("op-service-account-token");
|
|
77924
|
+
checks.push({
|
|
77925
|
+
name: "op-service-account-token",
|
|
77926
|
+
status: opToken ? "ok" : "warn",
|
|
77927
|
+
message: opToken ? void 0 : "Not found in keychain"
|
|
77928
|
+
});
|
|
77929
|
+
const ageKey = getFromKeychain("age-private-key");
|
|
77930
|
+
checks.push({
|
|
77931
|
+
name: "age-private-key",
|
|
77932
|
+
status: ageKey ? "ok" : "warn",
|
|
77933
|
+
message: ageKey ? void 0 : "Not found in keychain"
|
|
77934
|
+
});
|
|
77935
|
+
return checks;
|
|
77936
|
+
}
|
|
77937
|
+
function checkTools() {
|
|
77938
|
+
const checks = [];
|
|
77939
|
+
try {
|
|
77940
|
+
execSync3("gh auth status", { stdio: "pipe", timeout: 3e3 });
|
|
77941
|
+
checks.push({
|
|
77942
|
+
name: "gh CLI",
|
|
77943
|
+
status: "ok"
|
|
77944
|
+
});
|
|
77945
|
+
} catch {
|
|
77946
|
+
checks.push({
|
|
77947
|
+
name: "gh CLI",
|
|
77948
|
+
status: "warn",
|
|
77949
|
+
message: "Not installed or not authenticated"
|
|
77950
|
+
});
|
|
77951
|
+
}
|
|
77952
|
+
return checks;
|
|
77953
|
+
}
|
|
77954
|
+
function checkWorkspace() {
|
|
77955
|
+
const checks = [];
|
|
77956
|
+
const hivePath = join3(process.cwd(), ".hive");
|
|
77957
|
+
const hiveExists = existsSync6(hivePath);
|
|
77958
|
+
checks.push({
|
|
77959
|
+
name: ".hive directory",
|
|
77960
|
+
status: hiveExists ? "ok" : "warn",
|
|
77961
|
+
message: hiveExists ? void 0 : "Not found in current directory"
|
|
77962
|
+
});
|
|
77963
|
+
return checks;
|
|
77964
|
+
}
|
|
77965
|
+
function runHealthChecks() {
|
|
77966
|
+
const categories = [
|
|
77967
|
+
{
|
|
77968
|
+
category: "Environment",
|
|
77969
|
+
checks: checkEnvVars()
|
|
77970
|
+
},
|
|
77971
|
+
{
|
|
77972
|
+
category: "Keychain",
|
|
77973
|
+
checks: checkKeychain()
|
|
77974
|
+
},
|
|
77975
|
+
{
|
|
77976
|
+
category: "Tools",
|
|
77977
|
+
checks: checkTools()
|
|
77978
|
+
},
|
|
77979
|
+
{
|
|
77980
|
+
category: "Workspace",
|
|
77981
|
+
checks: checkWorkspace()
|
|
77982
|
+
}
|
|
77983
|
+
];
|
|
77984
|
+
let total = 0;
|
|
77985
|
+
let ok = 0;
|
|
77986
|
+
let warn = 0;
|
|
77987
|
+
let fail = 0;
|
|
77988
|
+
for (const category of categories) {
|
|
77989
|
+
for (const check of category.checks) {
|
|
77990
|
+
total++;
|
|
77991
|
+
if (check.status === "ok") ok++;
|
|
77992
|
+
else if (check.status === "warn") warn++;
|
|
77993
|
+
else fail++;
|
|
77994
|
+
}
|
|
77995
|
+
}
|
|
77996
|
+
let status;
|
|
77997
|
+
if (fail > 0) {
|
|
77998
|
+
status = "unhealthy";
|
|
77999
|
+
} else if (warn > 3) {
|
|
78000
|
+
status = "degraded";
|
|
78001
|
+
} else {
|
|
78002
|
+
status = "healthy";
|
|
78003
|
+
}
|
|
78004
|
+
return {
|
|
78005
|
+
status,
|
|
78006
|
+
categories,
|
|
78007
|
+
summary: { total, ok, warn, fail }
|
|
78008
|
+
};
|
|
78009
|
+
}
|
|
78010
|
+
function formatTextOutput(results) {
|
|
78011
|
+
console.log("\n\u{1FA7A} Health Check Results\n");
|
|
78012
|
+
for (const category of results.categories) {
|
|
78013
|
+
console.log(`${category.category}:`);
|
|
78014
|
+
for (const check of category.checks) {
|
|
78015
|
+
const symbol = check.status === "ok" ? "\u2713" : check.status === "warn" ? "\u26A0" : "\u2717";
|
|
78016
|
+
const line = check.message ? ` ${symbol} ${check.name} - ${check.message}` : ` ${symbol} ${check.name}`;
|
|
78017
|
+
console.log(line);
|
|
78018
|
+
}
|
|
78019
|
+
console.log("");
|
|
78020
|
+
}
|
|
78021
|
+
console.log("\u2500".repeat(60));
|
|
78022
|
+
console.log(
|
|
78023
|
+
`Summary: ${results.summary.ok}/${results.summary.total} checks passed`
|
|
78024
|
+
);
|
|
78025
|
+
if (results.summary.warn > 0) {
|
|
78026
|
+
console.log(`\u26A0 ${results.summary.warn} warnings`);
|
|
78027
|
+
}
|
|
78028
|
+
if (results.summary.fail > 0) {
|
|
78029
|
+
console.log(`\u2717 ${results.summary.fail} failures`);
|
|
78030
|
+
}
|
|
78031
|
+
console.log(`
|
|
78032
|
+
Overall status: ${results.status.toUpperCase()}`);
|
|
78033
|
+
console.log("");
|
|
78034
|
+
}
|
|
78035
|
+
function registerDoctorCommand(program3) {
|
|
78036
|
+
program3.command("doctor").description(
|
|
78037
|
+
"Run health checks on environment and tools\n\n Checks:\n - Environment variables\n - Keychain secrets\n - CLI tools (gh, op)\n - Workspace setup (.hive)\n\n Examples:\n skill doctor\n skill doctor --json"
|
|
78038
|
+
).option("--json", "Output as JSON").action(async (options, command) => {
|
|
78039
|
+
const ctx = await createContext({
|
|
78040
|
+
format: options.json ? "json" : "text",
|
|
78041
|
+
verbose: command.optsWithGlobals().verbose,
|
|
78042
|
+
quiet: command.optsWithGlobals().quiet
|
|
78043
|
+
});
|
|
78044
|
+
const results = runHealthChecks();
|
|
78045
|
+
if (options.json || ctx.format === "json") {
|
|
78046
|
+
ctx.output.data(results);
|
|
78047
|
+
} else {
|
|
78048
|
+
formatTextOutput(results);
|
|
78049
|
+
}
|
|
78050
|
+
});
|
|
78051
|
+
}
|
|
78052
|
+
|
|
77885
78053
|
// src/commands/eval.ts
|
|
77886
78054
|
init_esm_shims();
|
|
77887
78055
|
import { access, readFile as readFile2 } from "fs/promises";
|
|
@@ -79889,12 +80057,12 @@ async function scoreProduction(ctx, options) {
|
|
|
79889
80057
|
|
|
79890
80058
|
// src/commands/eval-local/seed.ts
|
|
79891
80059
|
init_esm_shims();
|
|
79892
|
-
import { join as
|
|
80060
|
+
import { join as join5 } from "path";
|
|
79893
80061
|
import { glob as glob3 } from "glob";
|
|
79894
80062
|
|
|
79895
80063
|
// src/lib/eval-seed.ts
|
|
79896
80064
|
init_esm_shims();
|
|
79897
|
-
import { join as
|
|
80065
|
+
import { join as join4 } from "path";
|
|
79898
80066
|
import { readFile as readFile5, readdir } from "fs/promises";
|
|
79899
80067
|
import { glob as glob2 } from "glob";
|
|
79900
80068
|
import matter from "gray-matter";
|
|
@@ -79940,7 +80108,7 @@ async function loadJsonFiles(dirPath) {
|
|
|
79940
80108
|
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
79941
80109
|
const items = await Promise.all(
|
|
79942
80110
|
jsonFiles.map(async (file) => {
|
|
79943
|
-
const content = await readFile5(
|
|
80111
|
+
const content = await readFile5(join4(dirPath, file), "utf-8");
|
|
79944
80112
|
return JSON.parse(content);
|
|
79945
80113
|
})
|
|
79946
80114
|
);
|
|
@@ -79950,7 +80118,7 @@ async function loadJsonFiles(dirPath) {
|
|
|
79950
80118
|
}
|
|
79951
80119
|
}
|
|
79952
80120
|
async function loadKnowledgeFiles(basePath) {
|
|
79953
|
-
const files = await glob2(
|
|
80121
|
+
const files = await glob2(join4(basePath, "**/*.md"));
|
|
79954
80122
|
const docs = [];
|
|
79955
80123
|
for (const filePath of files) {
|
|
79956
80124
|
const content = await readFile5(filePath, "utf-8");
|
|
@@ -80140,16 +80308,16 @@ async function seed(ctx, options) {
|
|
|
80140
80308
|
await cleanQdrant();
|
|
80141
80309
|
}
|
|
80142
80310
|
log("\u{1F4E6} Seeding apps...");
|
|
80143
|
-
const apps = await loadJsonFiles(
|
|
80311
|
+
const apps = await loadJsonFiles(join5(fixturesPath, "apps"));
|
|
80144
80312
|
result.apps = await seedApps(connection, apps);
|
|
80145
80313
|
log("\u{1F465} Loading customer fixtures...");
|
|
80146
|
-
const customers = await loadJsonFiles(
|
|
80314
|
+
const customers = await loadJsonFiles(join5(fixturesPath, "customers"));
|
|
80147
80315
|
result.customers = customers.length;
|
|
80148
80316
|
log("\u{1F4DA} Seeding knowledge base...");
|
|
80149
|
-
const knowledge = await loadKnowledgeFiles(
|
|
80317
|
+
const knowledge = await loadKnowledgeFiles(join5(fixturesPath, "knowledge"));
|
|
80150
80318
|
result.knowledge = knowledge.length;
|
|
80151
80319
|
result.embeddings = await seedKnowledgeBase(knowledge, !outputJson);
|
|
80152
|
-
const scenarioFiles = await glob3(
|
|
80320
|
+
const scenarioFiles = await glob3(join5(fixturesPath, "scenarios/**/*.json"));
|
|
80153
80321
|
result.scenarios = scenarioFiles.length;
|
|
80154
80322
|
await connection.end();
|
|
80155
80323
|
if (outputJson) {
|
|
@@ -80225,8 +80393,8 @@ init_esm_shims();
|
|
|
80225
80393
|
// src/commands/eval-pipeline/run.ts
|
|
80226
80394
|
init_esm_shims();
|
|
80227
80395
|
import { createHash as createHash2 } from "crypto";
|
|
80228
|
-
import { existsSync as
|
|
80229
|
-
import { join as
|
|
80396
|
+
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
80397
|
+
import { join as join6 } from "path";
|
|
80230
80398
|
import { readFile as readFile6 } from "fs/promises";
|
|
80231
80399
|
import { glob as glob4 } from "glob";
|
|
80232
80400
|
|
|
@@ -80639,11 +80807,11 @@ function getCacheKey(scenarioId, classifySourceHash) {
|
|
|
80639
80807
|
function getClassifySourceHash() {
|
|
80640
80808
|
try {
|
|
80641
80809
|
const possiblePaths = [
|
|
80642
|
-
|
|
80643
|
-
|
|
80810
|
+
join6(process.cwd(), "packages/core/src/pipeline/classify.ts"),
|
|
80811
|
+
join6(process.cwd(), "../core/src/pipeline/classify.ts")
|
|
80644
80812
|
];
|
|
80645
80813
|
for (const path of possiblePaths) {
|
|
80646
|
-
if (
|
|
80814
|
+
if (existsSync7(path)) {
|
|
80647
80815
|
const content = readFileSync5(path, "utf-8");
|
|
80648
80816
|
return createHash2("md5").update(content).digest("hex");
|
|
80649
80817
|
}
|
|
@@ -80653,9 +80821,9 @@ function getClassifySourceHash() {
|
|
|
80653
80821
|
return createHash2("md5").update(Math.floor(Date.now() / 3e5).toString()).digest("hex");
|
|
80654
80822
|
}
|
|
80655
80823
|
function loadCachedClassify(cacheKey) {
|
|
80656
|
-
const cachePath =
|
|
80824
|
+
const cachePath = join6(CACHE_DIR, `${cacheKey}.json`);
|
|
80657
80825
|
try {
|
|
80658
|
-
if (
|
|
80826
|
+
if (existsSync7(cachePath)) {
|
|
80659
80827
|
return JSON.parse(readFileSync5(cachePath, "utf-8"));
|
|
80660
80828
|
}
|
|
80661
80829
|
} catch {
|
|
@@ -80664,17 +80832,17 @@ function loadCachedClassify(cacheKey) {
|
|
|
80664
80832
|
}
|
|
80665
80833
|
function saveCachedClassify(cacheKey, result) {
|
|
80666
80834
|
try {
|
|
80667
|
-
if (!
|
|
80835
|
+
if (!existsSync7(CACHE_DIR)) {
|
|
80668
80836
|
mkdirSync(CACHE_DIR, { recursive: true });
|
|
80669
80837
|
}
|
|
80670
|
-
const cachePath =
|
|
80838
|
+
const cachePath = join6(CACHE_DIR, `${cacheKey}.json`);
|
|
80671
80839
|
writeFileSync4(cachePath, JSON.stringify(result));
|
|
80672
80840
|
} catch {
|
|
80673
80841
|
}
|
|
80674
80842
|
}
|
|
80675
80843
|
function clearClassifyCache() {
|
|
80676
80844
|
try {
|
|
80677
|
-
if (
|
|
80845
|
+
if (existsSync7(CACHE_DIR)) {
|
|
80678
80846
|
rmSync(CACHE_DIR, { recursive: true, force: true });
|
|
80679
80847
|
}
|
|
80680
80848
|
} catch {
|
|
@@ -81550,7 +81718,7 @@ Latency: ${avgLatency.toFixed(0)}ms avg`);
|
|
|
81550
81718
|
|
|
81551
81719
|
// src/commands/eval-pipeline/seed.ts
|
|
81552
81720
|
init_esm_shims();
|
|
81553
|
-
import { join as
|
|
81721
|
+
import { join as join7 } from "path";
|
|
81554
81722
|
import { glob as glob5 } from "glob";
|
|
81555
81723
|
async function seed2(ctx, options) {
|
|
81556
81724
|
const fixturesPath = options.fixtures || "fixtures";
|
|
@@ -81581,20 +81749,20 @@ async function seed2(ctx, options) {
|
|
|
81581
81749
|
await cleanQdrant();
|
|
81582
81750
|
}
|
|
81583
81751
|
if (!outputJson) ctx.output.message("\u{1F4E6} Seeding apps...");
|
|
81584
|
-
const apps = await loadJsonFiles(
|
|
81752
|
+
const apps = await loadJsonFiles(join7(fixturesPath, "apps"));
|
|
81585
81753
|
result.apps = await seedApps(connection, apps);
|
|
81586
81754
|
const [trustRows] = await connection.execute(
|
|
81587
81755
|
"SELECT COUNT(*) as count FROM SUPPORT_trust_scores"
|
|
81588
81756
|
);
|
|
81589
81757
|
result.trustScores = trustRows[0].count;
|
|
81590
81758
|
if (!outputJson) ctx.output.message("\u{1F465} Loading customer fixtures...");
|
|
81591
|
-
const customers = await loadJsonFiles(
|
|
81759
|
+
const customers = await loadJsonFiles(join7(fixturesPath, "customers"));
|
|
81592
81760
|
result.customers = customers.length;
|
|
81593
81761
|
if (!outputJson) ctx.output.message("\u{1F4DA} Seeding knowledge base...");
|
|
81594
|
-
const knowledge = await loadKnowledgeFiles(
|
|
81762
|
+
const knowledge = await loadKnowledgeFiles(join7(fixturesPath, "knowledge"));
|
|
81595
81763
|
result.knowledge = knowledge.length;
|
|
81596
81764
|
result.embeddings = await seedKnowledgeBase(knowledge, !outputJson);
|
|
81597
|
-
const scenarioFiles = await glob5(
|
|
81765
|
+
const scenarioFiles = await glob5(join7(fixturesPath, "scenarios/**/*.json"));
|
|
81598
81766
|
result.scenarios = scenarioFiles.length;
|
|
81599
81767
|
await connection.end();
|
|
81600
81768
|
if (outputJson) {
|
|
@@ -81666,7 +81834,7 @@ function registerEvalPipelineCommands(program3) {
|
|
|
81666
81834
|
|
|
81667
81835
|
// src/commands/eval-prompt.ts
|
|
81668
81836
|
init_esm_shims();
|
|
81669
|
-
import { existsSync as
|
|
81837
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
81670
81838
|
import { generateText as generateText2, stepCountIs as stepCountIs2, tool as tool4 } from "ai";
|
|
81671
81839
|
import { z as z5 } from "zod";
|
|
81672
81840
|
var leakPatterns = [
|
|
@@ -81853,7 +82021,7 @@ async function runEval2(ctx, options) {
|
|
|
81853
82021
|
try {
|
|
81854
82022
|
let prompt = SUPPORT_AGENT_PROMPT;
|
|
81855
82023
|
if (promptPath) {
|
|
81856
|
-
if (!
|
|
82024
|
+
if (!existsSync8(promptPath)) {
|
|
81857
82025
|
throw new CLIError({
|
|
81858
82026
|
userMessage: `Prompt file not found: ${promptPath}.`,
|
|
81859
82027
|
suggestion: "Verify the prompt path and try again."
|
|
@@ -81866,7 +82034,7 @@ async function runEval2(ctx, options) {
|
|
|
81866
82034
|
} else if (!outputJson) {
|
|
81867
82035
|
ctx.output.message("Using production prompt");
|
|
81868
82036
|
}
|
|
81869
|
-
if (!
|
|
82037
|
+
if (!existsSync8(datasetPath)) {
|
|
81870
82038
|
throw new CLIError({
|
|
81871
82039
|
userMessage: `Dataset not found: ${datasetPath}.`,
|
|
81872
82040
|
suggestion: "Provide a valid dataset file path."
|
|
@@ -81978,7 +82146,7 @@ async function comparePrompts(ctx, options) {
|
|
|
81978
82146
|
try {
|
|
81979
82147
|
const baselinePrompt = baseline ? readFileSync6(baseline, "utf-8") : SUPPORT_AGENT_PROMPT;
|
|
81980
82148
|
const candidatePrompt = readFileSync6(candidate, "utf-8");
|
|
81981
|
-
if (!
|
|
82149
|
+
if (!existsSync8(datasetPath)) {
|
|
81982
82150
|
throw new CLIError({
|
|
81983
82151
|
userMessage: `Dataset not found: ${datasetPath}.`,
|
|
81984
82152
|
suggestion: "Provide a valid dataset file path."
|
|
@@ -82126,20 +82294,20 @@ init_esm_shims();
|
|
|
82126
82294
|
|
|
82127
82295
|
// src/commands/faq/classify.ts
|
|
82128
82296
|
init_esm_shims();
|
|
82129
|
-
import { appendFileSync, existsSync as
|
|
82130
|
-
import { dirname as dirname2, join as
|
|
82297
|
+
import { appendFileSync, existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync7 } from "fs";
|
|
82298
|
+
import { dirname as dirname2, join as join8, resolve as resolve3 } from "path";
|
|
82131
82299
|
import { generateObject } from "ai";
|
|
82132
82300
|
import { z as z6 } from "zod";
|
|
82133
82301
|
var PROJECT_ROOT = resolve3(__dirname, "../../../..");
|
|
82134
|
-
var DEFAULT_PARQUET_PATH =
|
|
82302
|
+
var DEFAULT_PARQUET_PATH = join8(
|
|
82135
82303
|
PROJECT_ROOT,
|
|
82136
82304
|
"artifacts/phase-0/embeddings/v2/conversations.parquet"
|
|
82137
82305
|
);
|
|
82138
|
-
var DEFAULT_TAXONOMY_PATH =
|
|
82306
|
+
var DEFAULT_TAXONOMY_PATH = join8(
|
|
82139
82307
|
PROJECT_ROOT,
|
|
82140
82308
|
"artifacts/phase-1/llm-topics/taxonomy.json"
|
|
82141
82309
|
);
|
|
82142
|
-
var DEFAULT_OUTPUT_PATH =
|
|
82310
|
+
var DEFAULT_OUTPUT_PATH = join8(
|
|
82143
82311
|
PROJECT_ROOT,
|
|
82144
82312
|
"artifacts/phase-1/llm-topics/classifications.jsonl"
|
|
82145
82313
|
);
|
|
@@ -82173,7 +82341,7 @@ function createProgressReporter(ctx, total) {
|
|
|
82173
82341
|
};
|
|
82174
82342
|
}
|
|
82175
82343
|
async function loadConversationsFromParquet(parquetPath) {
|
|
82176
|
-
const { execSync:
|
|
82344
|
+
const { execSync: execSync4 } = await import("child_process");
|
|
82177
82345
|
const query = `
|
|
82178
82346
|
SELECT
|
|
82179
82347
|
conversation_id,
|
|
@@ -82184,7 +82352,7 @@ async function loadConversationsFromParquet(parquetPath) {
|
|
|
82184
82352
|
WHERE first_message IS NOT NULL
|
|
82185
82353
|
ORDER BY conversation_id
|
|
82186
82354
|
`;
|
|
82187
|
-
const result =
|
|
82355
|
+
const result = execSync4(`duckdb -json -c "${query.replace(/"/g, '\\"')}"`, {
|
|
82188
82356
|
encoding: "utf-8",
|
|
82189
82357
|
maxBuffer: 100 * 1024 * 1024
|
|
82190
82358
|
// 100MB buffer for large datasets
|
|
@@ -82194,7 +82362,7 @@ async function loadConversationsFromParquet(parquetPath) {
|
|
|
82194
82362
|
}
|
|
82195
82363
|
function loadExistingClassifications(outputPath) {
|
|
82196
82364
|
const classifiedIds = /* @__PURE__ */ new Set();
|
|
82197
|
-
if (!
|
|
82365
|
+
if (!existsSync9(outputPath)) {
|
|
82198
82366
|
return classifiedIds;
|
|
82199
82367
|
}
|
|
82200
82368
|
const content = readFileSync7(outputPath, "utf-8");
|
|
@@ -82312,7 +82480,7 @@ async function faqClassify(ctx, options) {
|
|
|
82312
82480
|
ctx.output.data(` Dry run: ${options.dryRun ?? false}`);
|
|
82313
82481
|
ctx.output.data("");
|
|
82314
82482
|
}
|
|
82315
|
-
if (!
|
|
82483
|
+
if (!existsSync9(parquetPath)) {
|
|
82316
82484
|
handleFaqClassifyError(
|
|
82317
82485
|
ctx,
|
|
82318
82486
|
new CLIError({
|
|
@@ -82323,7 +82491,7 @@ async function faqClassify(ctx, options) {
|
|
|
82323
82491
|
);
|
|
82324
82492
|
return;
|
|
82325
82493
|
}
|
|
82326
|
-
if (!
|
|
82494
|
+
if (!existsSync9(taxonomyPath)) {
|
|
82327
82495
|
handleFaqClassifyError(
|
|
82328
82496
|
ctx,
|
|
82329
82497
|
new CLIError({
|
|
@@ -82335,7 +82503,7 @@ async function faqClassify(ctx, options) {
|
|
|
82335
82503
|
return;
|
|
82336
82504
|
}
|
|
82337
82505
|
const outputDir = dirname2(outputPath);
|
|
82338
|
-
if (!
|
|
82506
|
+
if (!existsSync9(outputDir)) {
|
|
82339
82507
|
mkdirSync2(outputDir, { recursive: true });
|
|
82340
82508
|
}
|
|
82341
82509
|
if (!outputJson) ctx.output.data("\u{1F4DA} Loading taxonomy...");
|
|
@@ -82504,18 +82672,18 @@ function registerFaqClassifyCommands(program3) {
|
|
|
82504
82672
|
|
|
82505
82673
|
// src/commands/faq/cluster.ts
|
|
82506
82674
|
init_esm_shims();
|
|
82507
|
-
import { existsSync as
|
|
82508
|
-
import { join as
|
|
82675
|
+
import { existsSync as existsSync11 } from "fs";
|
|
82676
|
+
import { join as join10, resolve as resolve4 } from "path";
|
|
82509
82677
|
|
|
82510
82678
|
// ../core/src/faq/production-clusterer.ts
|
|
82511
82679
|
init_esm_shims();
|
|
82512
|
-
import { existsSync as
|
|
82513
|
-
import { join as
|
|
82680
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
82681
|
+
import { join as join9 } from "path";
|
|
82514
82682
|
function readPhase0Assignments(phase0Path) {
|
|
82515
|
-
const assignmentsPath =
|
|
82516
|
-
if (!
|
|
82517
|
-
const latestPath =
|
|
82518
|
-
if (!
|
|
82683
|
+
const assignmentsPath = join9(phase0Path, "clusters/v1/assignments.json");
|
|
82684
|
+
if (!existsSync10(assignmentsPath)) {
|
|
82685
|
+
const latestPath = join9(phase0Path, "clusters/latest/assignments.json");
|
|
82686
|
+
if (!existsSync10(latestPath)) {
|
|
82519
82687
|
throw new Error(`Phase 0 assignments not found at ${assignmentsPath}`);
|
|
82520
82688
|
}
|
|
82521
82689
|
const content2 = readFileSync8(latestPath, "utf-8");
|
|
@@ -82525,10 +82693,10 @@ function readPhase0Assignments(phase0Path) {
|
|
|
82525
82693
|
return JSON.parse(content);
|
|
82526
82694
|
}
|
|
82527
82695
|
function readPhase0Labels(phase0Path) {
|
|
82528
|
-
const labelsPath =
|
|
82529
|
-
if (!
|
|
82530
|
-
const latestPath =
|
|
82531
|
-
if (!
|
|
82696
|
+
const labelsPath = join9(phase0Path, "clusters/v1/labels.json");
|
|
82697
|
+
if (!existsSync10(labelsPath)) {
|
|
82698
|
+
const latestPath = join9(phase0Path, "clusters/latest/labels.json");
|
|
82699
|
+
if (!existsSync10(latestPath)) {
|
|
82532
82700
|
throw new Error(`Phase 0 labels not found at ${labelsPath}`);
|
|
82533
82701
|
}
|
|
82534
82702
|
const content2 = readFileSync8(latestPath, "utf-8");
|
|
@@ -82540,10 +82708,10 @@ function readPhase0Labels(phase0Path) {
|
|
|
82540
82708
|
return parsed.clusters || [];
|
|
82541
82709
|
}
|
|
82542
82710
|
function readPhase0Metrics(phase0Path) {
|
|
82543
|
-
const metricsPath =
|
|
82544
|
-
if (!
|
|
82545
|
-
const latestPath =
|
|
82546
|
-
if (!
|
|
82711
|
+
const metricsPath = join9(phase0Path, "clusters/v1/metrics.json");
|
|
82712
|
+
if (!existsSync10(metricsPath)) {
|
|
82713
|
+
const latestPath = join9(phase0Path, "clusters/latest/metrics.json");
|
|
82714
|
+
if (!existsSync10(latestPath)) {
|
|
82547
82715
|
throw new Error(`Phase 0 metrics not found at ${metricsPath}`);
|
|
82548
82716
|
}
|
|
82549
82717
|
return JSON.parse(readFileSync8(latestPath, "utf-8"));
|
|
@@ -82663,17 +82831,17 @@ async function generateProductionClustering(options) {
|
|
|
82663
82831
|
return result;
|
|
82664
82832
|
}
|
|
82665
82833
|
function writeProductionArtifacts(result, outputPath) {
|
|
82666
|
-
const versionPath =
|
|
82667
|
-
if (!
|
|
82834
|
+
const versionPath = join9(outputPath, result.version);
|
|
82835
|
+
if (!existsSync10(versionPath)) {
|
|
82668
82836
|
mkdirSync3(versionPath, { recursive: true });
|
|
82669
82837
|
}
|
|
82670
|
-
const resultPath =
|
|
82838
|
+
const resultPath = join9(versionPath, "clustering-result.json");
|
|
82671
82839
|
writeFileSync6(resultPath, JSON.stringify(result, null, 2));
|
|
82672
82840
|
console.log(`\u2705 Written: ${resultPath}`);
|
|
82673
|
-
const assignmentsPath =
|
|
82841
|
+
const assignmentsPath = join9(versionPath, "assignments.json");
|
|
82674
82842
|
writeFileSync6(assignmentsPath, JSON.stringify(result.assignments, null, 2));
|
|
82675
82843
|
console.log(`\u2705 Written: ${assignmentsPath}`);
|
|
82676
|
-
const clustersPath =
|
|
82844
|
+
const clustersPath = join9(versionPath, "clusters.json");
|
|
82677
82845
|
writeFileSync6(
|
|
82678
82846
|
clustersPath,
|
|
82679
82847
|
JSON.stringify(
|
|
@@ -82688,7 +82856,7 @@ function writeProductionArtifacts(result, outputPath) {
|
|
|
82688
82856
|
)
|
|
82689
82857
|
);
|
|
82690
82858
|
console.log(`\u2705 Written: ${clustersPath}`);
|
|
82691
|
-
const summaryPath =
|
|
82859
|
+
const summaryPath = join9(versionPath, "summary.json");
|
|
82692
82860
|
const summary = {
|
|
82693
82861
|
version: result.version,
|
|
82694
82862
|
generatedAt: result.generatedAt,
|
|
@@ -82702,8 +82870,8 @@ function writeProductionArtifacts(result, outputPath) {
|
|
|
82702
82870
|
};
|
|
82703
82871
|
writeFileSync6(summaryPath, JSON.stringify(summary, null, 2));
|
|
82704
82872
|
console.log(`\u2705 Written: ${summaryPath}`);
|
|
82705
|
-
const latestPath =
|
|
82706
|
-
if (
|
|
82873
|
+
const latestPath = join9(outputPath, "latest");
|
|
82874
|
+
if (existsSync10(latestPath)) {
|
|
82707
82875
|
const { rmSync: rmSync2 } = __require("fs");
|
|
82708
82876
|
rmSync2(latestPath, { recursive: true, force: true });
|
|
82709
82877
|
}
|
|
@@ -82714,8 +82882,8 @@ function writeProductionArtifacts(result, outputPath) {
|
|
|
82714
82882
|
"clusters.json",
|
|
82715
82883
|
"summary.json"
|
|
82716
82884
|
]) {
|
|
82717
|
-
const src =
|
|
82718
|
-
const dst =
|
|
82885
|
+
const src = join9(versionPath, file);
|
|
82886
|
+
const dst = join9(latestPath, file);
|
|
82719
82887
|
writeFileSync6(dst, readFileSync8(src));
|
|
82720
82888
|
}
|
|
82721
82889
|
console.log(`\u2705 Updated: ${latestPath}`);
|
|
@@ -82768,25 +82936,25 @@ function displayClusteringSummary(result) {
|
|
|
82768
82936
|
|
|
82769
82937
|
// src/commands/faq/cluster.ts
|
|
82770
82938
|
var PROJECT_ROOT2 = resolve4(__dirname, "../../../..");
|
|
82771
|
-
var DEFAULT_PHASE0_PATH =
|
|
82772
|
-
var DEFAULT_OUTPUT_PATH2 =
|
|
82939
|
+
var DEFAULT_PHASE0_PATH = join10(PROJECT_ROOT2, "artifacts/phase-0");
|
|
82940
|
+
var DEFAULT_OUTPUT_PATH2 = join10(PROJECT_ROOT2, "artifacts/phase-1/clustering");
|
|
82773
82941
|
function validatePaths(phase0Path) {
|
|
82774
|
-
const assignmentsPath =
|
|
82775
|
-
const labelsPath =
|
|
82776
|
-
const metricsPath =
|
|
82777
|
-
if (!
|
|
82942
|
+
const assignmentsPath = join10(phase0Path, "clusters/v1/assignments.json");
|
|
82943
|
+
const labelsPath = join10(phase0Path, "clusters/v1/labels.json");
|
|
82944
|
+
const metricsPath = join10(phase0Path, "clusters/v1/metrics.json");
|
|
82945
|
+
if (!existsSync11(assignmentsPath)) {
|
|
82778
82946
|
throw new CLIError({
|
|
82779
82947
|
userMessage: `Phase 0 assignments not found at ${assignmentsPath}.`,
|
|
82780
82948
|
suggestion: "Run Phase 0 clustering first or specify the correct --phase0-path."
|
|
82781
82949
|
});
|
|
82782
82950
|
}
|
|
82783
|
-
if (!
|
|
82951
|
+
if (!existsSync11(labelsPath)) {
|
|
82784
82952
|
throw new CLIError({
|
|
82785
82953
|
userMessage: `Phase 0 labels not found at ${labelsPath}.`,
|
|
82786
82954
|
suggestion: "Verify the --phase0-path points to valid artifacts."
|
|
82787
82955
|
});
|
|
82788
82956
|
}
|
|
82789
|
-
if (!
|
|
82957
|
+
if (!existsSync11(metricsPath)) {
|
|
82790
82958
|
throw new CLIError({
|
|
82791
82959
|
userMessage: `Phase 0 metrics not found at ${metricsPath}.`,
|
|
82792
82960
|
suggestion: "Verify the --phase0-path points to valid artifacts."
|
|
@@ -82824,7 +82992,7 @@ async function faqCluster(ctx, options) {
|
|
|
82824
82992
|
writeProductionArtifacts(result, outputPath);
|
|
82825
82993
|
if (!outputJson) {
|
|
82826
82994
|
ctx.output.data("\n\u2705 Production clustering complete!");
|
|
82827
|
-
ctx.output.data(` Artifacts written to: ${
|
|
82995
|
+
ctx.output.data(` Artifacts written to: ${join10(outputPath, version)}`);
|
|
82828
82996
|
}
|
|
82829
82997
|
} else {
|
|
82830
82998
|
if (!outputJson) ctx.output.data("\n\u{1F9EA} Dry run - no artifacts written");
|
|
@@ -82867,13 +83035,13 @@ function registerFaqClusterCommands(program3) {
|
|
|
82867
83035
|
|
|
82868
83036
|
// src/commands/faq/extract.ts
|
|
82869
83037
|
init_esm_shims();
|
|
82870
|
-
import { existsSync as
|
|
82871
|
-
import { join as
|
|
83038
|
+
import { existsSync as existsSync13 } from "fs";
|
|
83039
|
+
import { join as join12, resolve as resolve5 } from "path";
|
|
82872
83040
|
|
|
82873
83041
|
// ../core/src/faq/extractor.ts
|
|
82874
83042
|
init_esm_shims();
|
|
82875
|
-
import { existsSync as
|
|
82876
|
-
import { join as
|
|
83043
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
83044
|
+
import { join as join11 } from "path";
|
|
82877
83045
|
|
|
82878
83046
|
// ../core/src/faq/review.ts
|
|
82879
83047
|
init_esm_shims();
|
|
@@ -83487,14 +83655,14 @@ async function extractFaqCandidates(options) {
|
|
|
83487
83655
|
return result;
|
|
83488
83656
|
}
|
|
83489
83657
|
function writeExtractionArtifacts(result, outputPath) {
|
|
83490
|
-
const versionPath =
|
|
83491
|
-
if (!
|
|
83658
|
+
const versionPath = join11(outputPath, result.version);
|
|
83659
|
+
if (!existsSync12(versionPath)) {
|
|
83492
83660
|
mkdirSync4(versionPath, { recursive: true });
|
|
83493
83661
|
}
|
|
83494
|
-
const resultPath =
|
|
83662
|
+
const resultPath = join11(versionPath, "extraction-result.json");
|
|
83495
83663
|
writeFileSync7(resultPath, JSON.stringify(result, null, 2));
|
|
83496
83664
|
console.log(`\u2705 Written: ${resultPath}`);
|
|
83497
|
-
const candidatesPath =
|
|
83665
|
+
const candidatesPath = join11(versionPath, "candidates.json");
|
|
83498
83666
|
const candidatesData = {
|
|
83499
83667
|
version: result.version,
|
|
83500
83668
|
extractedAt: result.extractedAt,
|
|
@@ -83514,7 +83682,7 @@ function writeExtractionArtifacts(result, outputPath) {
|
|
|
83514
83682
|
};
|
|
83515
83683
|
writeFileSync7(candidatesPath, JSON.stringify(candidatesData, null, 2));
|
|
83516
83684
|
console.log(`\u2705 Written: ${candidatesPath}`);
|
|
83517
|
-
const statsPath =
|
|
83685
|
+
const statsPath = join11(versionPath, "stats.json");
|
|
83518
83686
|
writeFileSync7(
|
|
83519
83687
|
statsPath,
|
|
83520
83688
|
JSON.stringify(
|
|
@@ -83528,8 +83696,8 @@ function writeExtractionArtifacts(result, outputPath) {
|
|
|
83528
83696
|
)
|
|
83529
83697
|
);
|
|
83530
83698
|
console.log(`\u2705 Written: ${statsPath}`);
|
|
83531
|
-
const latestPath =
|
|
83532
|
-
if (
|
|
83699
|
+
const latestPath = join11(outputPath, "latest");
|
|
83700
|
+
if (existsSync12(latestPath)) {
|
|
83533
83701
|
const { rmSync: rmSync2 } = __require("fs");
|
|
83534
83702
|
rmSync2(latestPath, { recursive: true, force: true });
|
|
83535
83703
|
}
|
|
@@ -83539,9 +83707,9 @@ function writeExtractionArtifacts(result, outputPath) {
|
|
|
83539
83707
|
"candidates.json",
|
|
83540
83708
|
"stats.json"
|
|
83541
83709
|
]) {
|
|
83542
|
-
const src =
|
|
83543
|
-
const dst =
|
|
83544
|
-
if (
|
|
83710
|
+
const src = join11(versionPath, file);
|
|
83711
|
+
const dst = join11(latestPath, file);
|
|
83712
|
+
if (existsSync12(src)) {
|
|
83545
83713
|
writeFileSync7(dst, readFileSync9(src));
|
|
83546
83714
|
}
|
|
83547
83715
|
}
|
|
@@ -83595,24 +83763,24 @@ ${i + 1}. [${confPct}%]${golden} ${candidate.suggestedCategory}`
|
|
|
83595
83763
|
|
|
83596
83764
|
// src/commands/faq/extract.ts
|
|
83597
83765
|
var PROJECT_ROOT3 = resolve5(__dirname, "../../../..");
|
|
83598
|
-
var DEFAULT_CLUSTERING_PATH =
|
|
83766
|
+
var DEFAULT_CLUSTERING_PATH = join12(
|
|
83599
83767
|
PROJECT_ROOT3,
|
|
83600
83768
|
"artifacts/phase-1/clustering/v1/clustering-result.json"
|
|
83601
83769
|
);
|
|
83602
|
-
var DEFAULT_GOLDEN_PATH =
|
|
83770
|
+
var DEFAULT_GOLDEN_PATH = join12(
|
|
83603
83771
|
PROJECT_ROOT3,
|
|
83604
83772
|
"artifacts/phase-0/golden/latest/responses.json"
|
|
83605
83773
|
);
|
|
83606
|
-
var DEFAULT_OUTPUT_PATH3 =
|
|
83774
|
+
var DEFAULT_OUTPUT_PATH3 = join12(PROJECT_ROOT3, "artifacts/phase-1/extraction");
|
|
83607
83775
|
var DEFAULT_CACHE_PATH = `${process.env.HOME}/skill/data/front-cache.db`;
|
|
83608
83776
|
function validatePaths2(ctx, clusteringPath, goldenPath, outputJson) {
|
|
83609
|
-
if (!
|
|
83777
|
+
if (!existsSync13(clusteringPath)) {
|
|
83610
83778
|
throw new CLIError({
|
|
83611
83779
|
userMessage: `Clustering result not found at ${clusteringPath}.`,
|
|
83612
83780
|
suggestion: "Run `bun src/index.ts faq cluster` first to generate clustering."
|
|
83613
83781
|
});
|
|
83614
83782
|
}
|
|
83615
|
-
if (goldenPath && !
|
|
83783
|
+
if (goldenPath && !existsSync13(goldenPath)) {
|
|
83616
83784
|
if (!outputJson) {
|
|
83617
83785
|
ctx.output.warn(`Golden responses not found at ${goldenPath}`);
|
|
83618
83786
|
ctx.output.warn("Golden matching will be disabled.");
|
|
@@ -83641,7 +83809,7 @@ async function faqExtract(ctx, options) {
|
|
|
83641
83809
|
ctx.output.data("");
|
|
83642
83810
|
}
|
|
83643
83811
|
validatePaths2(ctx, clusteringPath, goldenPath, outputJson);
|
|
83644
|
-
if (!
|
|
83812
|
+
if (!existsSync13(cachePath)) {
|
|
83645
83813
|
const cliError = new CLIError({
|
|
83646
83814
|
userMessage: `DuckDB cache not found at ${cachePath}.`,
|
|
83647
83815
|
suggestion: "Run `bun src/index.ts front-cache sync` first to populate cache."
|
|
@@ -83663,7 +83831,7 @@ async function faqExtract(ctx, options) {
|
|
|
83663
83831
|
}
|
|
83664
83832
|
const extractionOptions = {
|
|
83665
83833
|
clusteringPath,
|
|
83666
|
-
goldenPath:
|
|
83834
|
+
goldenPath: existsSync13(goldenPath) ? goldenPath : void 0,
|
|
83667
83835
|
source,
|
|
83668
83836
|
outputPath,
|
|
83669
83837
|
version,
|
|
@@ -83708,7 +83876,7 @@ async function faqExtract(ctx, options) {
|
|
|
83708
83876
|
if (!options.dryRun) {
|
|
83709
83877
|
ctx.output.data(`
|
|
83710
83878
|
\u2705 Extraction complete!`);
|
|
83711
|
-
ctx.output.data(` Artifacts written to: ${
|
|
83879
|
+
ctx.output.data(` Artifacts written to: ${join12(outputPath, version)}`);
|
|
83712
83880
|
if (options.pushRedis && options.app) {
|
|
83713
83881
|
ctx.output.data(
|
|
83714
83882
|
` Candidates pushed to Redis queue: faq:pending:${options.app}`
|
|
@@ -92737,9 +92905,9 @@ function registerFaqMineCommands(program3) {
|
|
|
92737
92905
|
// src/commands/faq/review.ts
|
|
92738
92906
|
init_esm_shims();
|
|
92739
92907
|
import { spawnSync } from "child_process";
|
|
92740
|
-
import { existsSync as
|
|
92908
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
|
|
92741
92909
|
import { tmpdir } from "os";
|
|
92742
|
-
import { join as
|
|
92910
|
+
import { join as join13 } from "path";
|
|
92743
92911
|
import { confirm as confirm2, select as select3 } from "@inquirer/prompts";
|
|
92744
92912
|
var COLORS2 = {
|
|
92745
92913
|
reset: "\x1B[0m",
|
|
@@ -92800,7 +92968,7 @@ function getEditor() {
|
|
|
92800
92968
|
}
|
|
92801
92969
|
function editInEditor(ctx, question, answer) {
|
|
92802
92970
|
const editor = getEditor();
|
|
92803
|
-
const tmpFile =
|
|
92971
|
+
const tmpFile = join13(tmpdir(), `faq-edit-${Date.now()}.md`);
|
|
92804
92972
|
const content = `# FAQ Edit
|
|
92805
92973
|
|
|
92806
92974
|
## Question
|
|
@@ -92843,7 +93011,7 @@ The sections are separated by "## Question" and "## Answer" headers.
|
|
|
92843
93011
|
answer: editedAnswer
|
|
92844
93012
|
};
|
|
92845
93013
|
} finally {
|
|
92846
|
-
if (
|
|
93014
|
+
if (existsSync14(tmpFile)) {
|
|
92847
93015
|
unlinkSync(tmpFile);
|
|
92848
93016
|
}
|
|
92849
93017
|
}
|
|
@@ -97795,6 +97963,154 @@ function registerInvestigateCommands(inngest) {
|
|
|
97795
97963
|
});
|
|
97796
97964
|
}
|
|
97797
97965
|
|
|
97966
|
+
// src/commands/inngest/patterns.ts
|
|
97967
|
+
init_esm_shims();
|
|
97968
|
+
async function patterns(ctx, options) {
|
|
97969
|
+
const outputJson = options.json === true || ctx.format === "json";
|
|
97970
|
+
try {
|
|
97971
|
+
const client = new InngestClient({ dev: options.dev });
|
|
97972
|
+
const params = { limit: 100 };
|
|
97973
|
+
if (options.after) {
|
|
97974
|
+
params.received_after = parseTimeArg(options.after);
|
|
97975
|
+
}
|
|
97976
|
+
const events = outputJson ? await client.listEvents(params) : await withSpinner("Fetching events...", () => client.listEvents(params));
|
|
97977
|
+
const byName = {};
|
|
97978
|
+
const byFunction = {};
|
|
97979
|
+
for (const event of events.data) {
|
|
97980
|
+
byName[event.name] = (byName[event.name] || 0) + 1;
|
|
97981
|
+
if (event.name === "inngest/function.finished") {
|
|
97982
|
+
const data2 = event.data;
|
|
97983
|
+
const functionId = data2?.function_id;
|
|
97984
|
+
if (!functionId) continue;
|
|
97985
|
+
if (!byFunction[functionId]) {
|
|
97986
|
+
byFunction[functionId] = { success: 0, failed: 0 };
|
|
97987
|
+
}
|
|
97988
|
+
if (data2.error || data2._inngest?.status === "Failed") {
|
|
97989
|
+
byFunction[functionId].failed++;
|
|
97990
|
+
} else {
|
|
97991
|
+
byFunction[functionId].success++;
|
|
97992
|
+
}
|
|
97993
|
+
}
|
|
97994
|
+
}
|
|
97995
|
+
const timeRangeHours = options.after ? parseTimeWindow(options.after) : 24;
|
|
97996
|
+
const topEvents = Object.entries(byName).map(([name, count]) => ({
|
|
97997
|
+
name,
|
|
97998
|
+
count,
|
|
97999
|
+
frequency_per_hour: count / timeRangeHours
|
|
98000
|
+
})).sort((a, b) => b.count - a.count).slice(0, 10);
|
|
98001
|
+
const functionStats = {};
|
|
98002
|
+
for (const [functionId, stats4] of Object.entries(byFunction)) {
|
|
98003
|
+
const total = stats4.success + stats4.failed;
|
|
98004
|
+
functionStats[functionId] = {
|
|
98005
|
+
success: stats4.success,
|
|
98006
|
+
failed: stats4.failed,
|
|
98007
|
+
success_rate: total > 0 ? stats4.success / total : 0
|
|
98008
|
+
};
|
|
98009
|
+
}
|
|
98010
|
+
const output = {
|
|
98011
|
+
time_range: options.after || "24h",
|
|
98012
|
+
total_events: events.data.length,
|
|
98013
|
+
events_by_name: byName,
|
|
98014
|
+
by_function: functionStats,
|
|
98015
|
+
top_events: topEvents
|
|
98016
|
+
};
|
|
98017
|
+
if (outputJson) {
|
|
98018
|
+
ctx.output.data(output);
|
|
98019
|
+
} else {
|
|
98020
|
+
const lines = [];
|
|
98021
|
+
lines.push("\nEvent Patterns\n");
|
|
98022
|
+
lines.push(`Time range: ${output.time_range}`);
|
|
98023
|
+
lines.push(`Total events: ${output.total_events}
|
|
98024
|
+
`);
|
|
98025
|
+
lines.push("Top Events:");
|
|
98026
|
+
lines.push(
|
|
98027
|
+
"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"
|
|
98028
|
+
);
|
|
98029
|
+
lines.push(
|
|
98030
|
+
"\u2502 Event Name \u2502 Count \u2502 Per Hour \u2502"
|
|
98031
|
+
);
|
|
98032
|
+
lines.push(
|
|
98033
|
+
"\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"
|
|
98034
|
+
);
|
|
98035
|
+
for (const event of topEvents) {
|
|
98036
|
+
const name = event.name.padEnd(38).slice(0, 38);
|
|
98037
|
+
const count = String(event.count).padStart(5);
|
|
98038
|
+
const freq = event.frequency_per_hour.toFixed(2).padStart(12);
|
|
98039
|
+
lines.push(`\u2502 ${name} \u2502 ${count} \u2502 ${freq} \u2502`);
|
|
98040
|
+
}
|
|
98041
|
+
lines.push(
|
|
98042
|
+
"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n"
|
|
98043
|
+
);
|
|
98044
|
+
if (Object.keys(functionStats).length > 0) {
|
|
98045
|
+
lines.push("Function Stats:");
|
|
98046
|
+
lines.push(
|
|
98047
|
+
"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"
|
|
98048
|
+
);
|
|
98049
|
+
lines.push(
|
|
98050
|
+
"\u2502 Function \u2502 Success \u2502 Failed \u2502 Success % \u2502"
|
|
98051
|
+
);
|
|
98052
|
+
lines.push(
|
|
98053
|
+
"\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"
|
|
98054
|
+
);
|
|
98055
|
+
for (const [functionId, stats4] of Object.entries(functionStats)) {
|
|
98056
|
+
const name = functionId.padEnd(38).slice(0, 38);
|
|
98057
|
+
const success = String(stats4.success).padStart(7);
|
|
98058
|
+
const failed = String(stats4.failed).padStart(6);
|
|
98059
|
+
const rate = `${(stats4.success_rate * 100).toFixed(1)}%`.padStart(11);
|
|
98060
|
+
lines.push(`\u2502 ${name} \u2502 ${success} \u2502 ${failed} \u2502 ${rate} \u2502`);
|
|
98061
|
+
}
|
|
98062
|
+
lines.push(
|
|
98063
|
+
"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n"
|
|
98064
|
+
);
|
|
98065
|
+
}
|
|
98066
|
+
lines.push(
|
|
98067
|
+
"Use `skill inngest failures` for detailed failure analysis.\n"
|
|
98068
|
+
);
|
|
98069
|
+
ctx.output.data(lines.join("\n"));
|
|
98070
|
+
}
|
|
98071
|
+
} catch (error) {
|
|
98072
|
+
const cliError = error instanceof CLIError ? error : new CLIError({
|
|
98073
|
+
userMessage: "Failed to analyze Inngest patterns.",
|
|
98074
|
+
suggestion: "Verify INNGEST_SIGNING_KEY and time window.",
|
|
98075
|
+
cause: error
|
|
98076
|
+
});
|
|
98077
|
+
ctx.output.error(formatError(cliError));
|
|
98078
|
+
process.exitCode = cliError.exitCode;
|
|
98079
|
+
}
|
|
98080
|
+
}
|
|
98081
|
+
function parseTimeWindow(input2) {
|
|
98082
|
+
const match = input2.match(/^(\d+)([hmd])$/);
|
|
98083
|
+
if (!match) return 24;
|
|
98084
|
+
const [, num, unit] = match;
|
|
98085
|
+
const value = Number.parseInt(num ?? "24", 10);
|
|
98086
|
+
switch (unit) {
|
|
98087
|
+
case "h":
|
|
98088
|
+
return value;
|
|
98089
|
+
case "d":
|
|
98090
|
+
return value * 24;
|
|
98091
|
+
case "m":
|
|
98092
|
+
return value / 60;
|
|
98093
|
+
default:
|
|
98094
|
+
return 24;
|
|
98095
|
+
}
|
|
98096
|
+
}
|
|
98097
|
+
function registerPatternsCommand(inngest) {
|
|
98098
|
+
inngest.command("patterns").description(
|
|
98099
|
+
"Aggregate event analysis (event distribution, function success rates)"
|
|
98100
|
+
).option("--after <time>", 'Time window (e.g., "2h", "1d")', "24h").option("--json", "Output as JSON").option("--dev", "Use dev server").action(async (options, command) => {
|
|
98101
|
+
const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
|
|
98102
|
+
...command.parent?.opts(),
|
|
98103
|
+
...command.opts()
|
|
98104
|
+
};
|
|
98105
|
+
const ctx = await createContext({
|
|
98106
|
+
format: options.json ? "json" : opts.format,
|
|
98107
|
+
verbose: opts.verbose,
|
|
98108
|
+
quiet: opts.quiet
|
|
98109
|
+
});
|
|
98110
|
+
await patterns(ctx, options);
|
|
98111
|
+
});
|
|
98112
|
+
}
|
|
98113
|
+
|
|
97798
98114
|
// src/commands/inngest/runs.ts
|
|
97799
98115
|
init_esm_shims();
|
|
97800
98116
|
import { confirm as confirm4 } from "@inquirer/prompts";
|
|
@@ -97984,11 +98300,16 @@ function registerSignalCommand(inngest) {
|
|
|
97984
98300
|
|
|
97985
98301
|
// src/commands/inngest/index.ts
|
|
97986
98302
|
function registerInngestCommands(program3, usageState2) {
|
|
97987
|
-
const
|
|
98303
|
+
const baseDescription = getInngestAdaptiveDescription(usageState2);
|
|
98304
|
+
const descriptionWithEnv = `${baseDescription}
|
|
98305
|
+
|
|
98306
|
+
Environment: INNGEST_SIGNING_KEY required. Run \`skill doctor\` to check.`;
|
|
98307
|
+
const inngest = program3.command("inngest").description(descriptionWithEnv);
|
|
97988
98308
|
registerEventsCommands(inngest);
|
|
97989
98309
|
registerRunsCommands(inngest);
|
|
97990
98310
|
registerSignalCommand(inngest);
|
|
97991
98311
|
registerInvestigateCommands(inngest);
|
|
98312
|
+
registerPatternsCommand(inngest);
|
|
97992
98313
|
}
|
|
97993
98314
|
|
|
97994
98315
|
// src/commands/kb-sync.ts
|
|
@@ -114017,7 +114338,7 @@ function registerKbCommands(program3) {
|
|
|
114017
114338
|
|
|
114018
114339
|
// src/commands/keys/index.ts
|
|
114019
114340
|
init_esm_shims();
|
|
114020
|
-
import { existsSync as
|
|
114341
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
114021
114342
|
import { password as password2, select as select5 } from "@inquirer/prompts";
|
|
114022
114343
|
import { Decrypter as Decrypter4 } from "age-encryption";
|
|
114023
114344
|
var buildContext4 = async (command, json) => {
|
|
@@ -114034,7 +114355,7 @@ var buildContext4 = async (command, json) => {
|
|
|
114034
114355
|
async function getUserConfiguredKeys() {
|
|
114035
114356
|
const keyPath = getAgeKeyPath();
|
|
114036
114357
|
const configPath = getEncryptedConfigPath();
|
|
114037
|
-
if (!
|
|
114358
|
+
if (!existsSync15(keyPath) || !existsSync15(configPath)) {
|
|
114038
114359
|
return /* @__PURE__ */ new Set();
|
|
114039
114360
|
}
|
|
114040
114361
|
try {
|
|
@@ -114102,7 +114423,7 @@ async function showKeyStatus(ctx) {
|
|
|
114102
114423
|
}
|
|
114103
114424
|
async function interactiveKeySetup(ctx) {
|
|
114104
114425
|
const keyPath = getAgeKeyPath();
|
|
114105
|
-
if (!
|
|
114426
|
+
if (!existsSync15(keyPath)) {
|
|
114106
114427
|
ctx.output.data("\n\u{1F511} First time setup - creating your encryption key...\n");
|
|
114107
114428
|
await configInitAction(ctx, { json: false });
|
|
114108
114429
|
ctx.output.data("");
|
|
@@ -114195,7 +114516,7 @@ function registerKeysCommands(program3) {
|
|
|
114195
114516
|
).option("--json", "Output as JSON").action(async (keyValue, options, command) => {
|
|
114196
114517
|
const ctx = await buildContext4(command, options.json);
|
|
114197
114518
|
const keyPath = getAgeKeyPath();
|
|
114198
|
-
if (!
|
|
114519
|
+
if (!existsSync15(keyPath)) {
|
|
114199
114520
|
if (process.stdin.isTTY && !options.json) {
|
|
114200
114521
|
ctx.output.data(
|
|
114201
114522
|
"\u{1F511} First time setup - creating your encryption key...\n"
|
|
@@ -114244,8 +114565,8 @@ function registerKeysCommands(program3) {
|
|
|
114244
114565
|
steps.push("\u2713 OP token from env");
|
|
114245
114566
|
}
|
|
114246
114567
|
if (!opToken) {
|
|
114247
|
-
const { getFromKeychain } = await import("./keychain-IEZHT5WN.js");
|
|
114248
|
-
opToken =
|
|
114568
|
+
const { getFromKeychain: getFromKeychain2 } = await import("./keychain-IEZHT5WN.js");
|
|
114569
|
+
opToken = getFromKeychain2("op-service-account-token");
|
|
114249
114570
|
if (opToken) steps.push("\u2713 OP token from keychain");
|
|
114250
114571
|
}
|
|
114251
114572
|
if (!opToken && status.opCliAvailable) {
|
|
@@ -114265,8 +114586,8 @@ function registerKeysCommands(program3) {
|
|
|
114265
114586
|
steps.push("\u2713 Age key from env");
|
|
114266
114587
|
}
|
|
114267
114588
|
if (!ageKey) {
|
|
114268
|
-
const { getFromKeychain } = await import("./keychain-IEZHT5WN.js");
|
|
114269
|
-
ageKey =
|
|
114589
|
+
const { getFromKeychain: getFromKeychain2 } = await import("./keychain-IEZHT5WN.js");
|
|
114590
|
+
ageKey = getFromKeychain2("age-private-key");
|
|
114270
114591
|
if (ageKey) steps.push("\u2713 Age key from keychain");
|
|
114271
114592
|
}
|
|
114272
114593
|
if (!ageKey && opToken) {
|
|
@@ -115637,6 +115958,25 @@ var PRIORITY_EMOJI2 = {
|
|
|
115637
115958
|
3: "\u{1F7E2}",
|
|
115638
115959
|
4: "\u26AA"
|
|
115639
115960
|
};
|
|
115961
|
+
function parseRelativeTime(timeStr) {
|
|
115962
|
+
const match = timeStr.match(/^(\d+)(d|w|h|m)$/);
|
|
115963
|
+
if (!match || !match[1] || !match[2]) {
|
|
115964
|
+
throw new CLIError({
|
|
115965
|
+
userMessage: `Invalid time format: ${timeStr}`,
|
|
115966
|
+
suggestion: "Use format like 90d, 2w, 24h, or 3m"
|
|
115967
|
+
});
|
|
115968
|
+
}
|
|
115969
|
+
const value = parseInt(match[1], 10);
|
|
115970
|
+
const unit = match[2];
|
|
115971
|
+
const msMap = {
|
|
115972
|
+
h: 60 * 60 * 1e3,
|
|
115973
|
+
d: 24 * 60 * 60 * 1e3,
|
|
115974
|
+
w: 7 * 24 * 60 * 60 * 1e3,
|
|
115975
|
+
m: 30 * 24 * 60 * 60 * 1e3
|
|
115976
|
+
};
|
|
115977
|
+
const ms = value * msMap[unit];
|
|
115978
|
+
return new Date(Date.now() - ms);
|
|
115979
|
+
}
|
|
115640
115980
|
async function listIssues(ctx, options = {}) {
|
|
115641
115981
|
const limit2 = options.limit || 20;
|
|
115642
115982
|
try {
|
|
@@ -115648,6 +115988,10 @@ async function listIssues(ctx, options = {}) {
|
|
|
115648
115988
|
}
|
|
115649
115989
|
}
|
|
115650
115990
|
};
|
|
115991
|
+
if (options.olderThan) {
|
|
115992
|
+
const date = parseRelativeTime(options.olderThan);
|
|
115993
|
+
filter4.updatedAt = { lt: date };
|
|
115994
|
+
}
|
|
115651
115995
|
let teamKey;
|
|
115652
115996
|
if (options.team) {
|
|
115653
115997
|
const teams = await client.teams();
|
|
@@ -115709,28 +116053,57 @@ async function listIssues(ctx, options = {}) {
|
|
|
115709
116053
|
});
|
|
115710
116054
|
const issues = response.nodes || [];
|
|
115711
116055
|
const issuesWithDetails = await Promise.all(
|
|
115712
|
-
issues.map(async (issue) =>
|
|
115713
|
-
issue
|
|
115714
|
-
|
|
115715
|
-
|
|
115716
|
-
|
|
115717
|
-
|
|
116056
|
+
issues.map(async (issue) => {
|
|
116057
|
+
const labels = options.export ? await issue.labels() : null;
|
|
116058
|
+
const comments = options.export ? await issue.comments() : null;
|
|
116059
|
+
return {
|
|
116060
|
+
issue,
|
|
116061
|
+
state: await issue.state,
|
|
116062
|
+
assignee: await issue.assignee,
|
|
116063
|
+
team: await issue.team,
|
|
116064
|
+
labels: labels?.nodes || null,
|
|
116065
|
+
comments: comments?.nodes || null
|
|
116066
|
+
};
|
|
116067
|
+
})
|
|
115718
116068
|
);
|
|
115719
116069
|
if (ctx.format === "json") {
|
|
115720
|
-
const issueData =
|
|
115721
|
-
(
|
|
115722
|
-
|
|
115723
|
-
|
|
115724
|
-
|
|
115725
|
-
|
|
115726
|
-
|
|
115727
|
-
|
|
115728
|
-
|
|
115729
|
-
|
|
115730
|
-
|
|
115731
|
-
|
|
115732
|
-
|
|
115733
|
-
|
|
116070
|
+
const issueData = await Promise.all(
|
|
116071
|
+
issuesWithDetails.map(
|
|
116072
|
+
async ({ issue, state, assignee, team, labels, comments }) => {
|
|
116073
|
+
const baseData = {
|
|
116074
|
+
id: issue.id,
|
|
116075
|
+
identifier: issue.identifier,
|
|
116076
|
+
title: issue.title,
|
|
116077
|
+
state: state?.name || null,
|
|
116078
|
+
stateType: state?.type || null,
|
|
116079
|
+
priority: issue.priority,
|
|
116080
|
+
assignee: assignee ? {
|
|
116081
|
+
id: assignee.id,
|
|
116082
|
+
name: assignee.name,
|
|
116083
|
+
email: assignee.email
|
|
116084
|
+
} : null,
|
|
116085
|
+
team: team ? { key: team.key, name: team.name } : null,
|
|
116086
|
+
url: issue.url,
|
|
116087
|
+
createdAt: issue.createdAt,
|
|
116088
|
+
updatedAt: issue.updatedAt
|
|
116089
|
+
};
|
|
116090
|
+
if (options.export) {
|
|
116091
|
+
return {
|
|
116092
|
+
...baseData,
|
|
116093
|
+
description: issue.description,
|
|
116094
|
+
labels: labels ? labels.map((l) => l.name) : [],
|
|
116095
|
+
comments: comments ? await Promise.all(
|
|
116096
|
+
comments.map(async (c) => ({
|
|
116097
|
+
body: c.body,
|
|
116098
|
+
user: (await c.user)?.name || "Unknown",
|
|
116099
|
+
createdAt: c.createdAt
|
|
116100
|
+
}))
|
|
116101
|
+
) : []
|
|
116102
|
+
};
|
|
116103
|
+
}
|
|
116104
|
+
return baseData;
|
|
116105
|
+
}
|
|
116106
|
+
)
|
|
115734
116107
|
);
|
|
115735
116108
|
ctx.output.data(
|
|
115736
116109
|
JSON.stringify(
|
|
@@ -115778,7 +116151,14 @@ async function listIssues(ctx, options = {}) {
|
|
|
115778
116151
|
ctx.output.data("");
|
|
115779
116152
|
return;
|
|
115780
116153
|
}
|
|
115781
|
-
for (const {
|
|
116154
|
+
for (const {
|
|
116155
|
+
issue,
|
|
116156
|
+
state,
|
|
116157
|
+
assignee,
|
|
116158
|
+
team,
|
|
116159
|
+
labels,
|
|
116160
|
+
comments
|
|
116161
|
+
} of issuesWithDetails) {
|
|
115782
116162
|
const emoji = PRIORITY_EMOJI2[issue.priority] || "\u26AA";
|
|
115783
116163
|
const assigneeName = assignee ? `@${assignee.name}` : "";
|
|
115784
116164
|
const teamBadge = team ? `[${team.key}]` : "";
|
|
@@ -115789,6 +116169,19 @@ async function listIssues(ctx, options = {}) {
|
|
|
115789
116169
|
ctx.output.data(
|
|
115790
116170
|
` Status: ${state?.name || "unknown"}${assigneeName ? ` | Assignee: ${assigneeName}` : ""}`
|
|
115791
116171
|
);
|
|
116172
|
+
if (options.export) {
|
|
116173
|
+
if (issue.description) {
|
|
116174
|
+
ctx.output.data(` Description: ${issue.description}`);
|
|
116175
|
+
}
|
|
116176
|
+
if (labels && labels.length > 0) {
|
|
116177
|
+
ctx.output.data(
|
|
116178
|
+
` Labels: ${labels.map((l) => l.name).join(", ")}`
|
|
116179
|
+
);
|
|
116180
|
+
}
|
|
116181
|
+
if (comments && comments.length > 0) {
|
|
116182
|
+
ctx.output.data(` Comments: ${comments.length}`);
|
|
116183
|
+
}
|
|
116184
|
+
}
|
|
115792
116185
|
}
|
|
115793
116186
|
ctx.output.data("");
|
|
115794
116187
|
ctx.output.data(" Use `skill linear issue <ID> --json` for full details.");
|
|
@@ -116582,6 +116975,13 @@ Quick start:
|
|
|
116582
116975
|
skill linear create "Title" Create issue
|
|
116583
116976
|
skill linear search "query" Search issues
|
|
116584
116977
|
|
|
116978
|
+
Bulk queries:
|
|
116979
|
+
skill linear issues --older-than 90d Stale issues
|
|
116980
|
+
skill linear issues --older-than 2w --export Full data export
|
|
116981
|
+
|
|
116982
|
+
Environment:
|
|
116983
|
+
LINEAR_API_KEY required. Run 'skill doctor' to check.
|
|
116984
|
+
|
|
116585
116985
|
All commands support --json for machine-readable output.
|
|
116586
116986
|
|
|
116587
116987
|
\u26A0\uFE0F Write operations require personal LINEAR_API_KEY (run 'skill keys add').`
|
|
@@ -116594,8 +116994,10 @@ Examples:
|
|
|
116594
116994
|
skill linear issues --team ENG Filter by team
|
|
116595
116995
|
skill linear issues --state "In Progress" Filter by state
|
|
116596
116996
|
skill linear issues --assignee me Your issues
|
|
116597
|
-
skill linear issues --priority 0 Urgent only
|
|
116598
|
-
|
|
116997
|
+
skill linear issues --priority 0 Urgent only
|
|
116998
|
+
skill linear issues --older-than 90d Stale issues (90+ days)
|
|
116999
|
+
skill linear issues --older-than 2w --export Full export`
|
|
117000
|
+
).option("--limit <number>", "Maximum results (default: 20)", "20").option("--team <key>", "Filter by team key (e.g., ENG)").option("--state <name>", "Filter by state name").option("--assignee <email>", 'Filter by assignee (or "me")').option("--project <name>", "Filter by project name").option("--priority <0-4>", "Filter by priority (0=urgent)").option("--older-than <time>", "Filter by last update (90d, 2w, 24h, 3m)").option("--export", "Include full details (description, labels, comments)").option("--json", "Output as JSON with HATEOAS links").action(async (options, command) => {
|
|
116599
117001
|
const ctx = await contextFromCommand2(command, options);
|
|
116600
117002
|
await listIssues(ctx, {
|
|
116601
117003
|
limit: parseInt(options.limit || "20", 10),
|
|
@@ -116603,7 +117005,9 @@ Examples:
|
|
|
116603
117005
|
state: options.state,
|
|
116604
117006
|
assignee: options.assignee,
|
|
116605
117007
|
project: options.project,
|
|
116606
|
-
priority: options.priority !== void 0 ? parseInt(options.priority, 10) : void 0
|
|
117008
|
+
priority: options.priority !== void 0 ? parseInt(options.priority, 10) : void 0,
|
|
117009
|
+
olderThan: options.olderThan,
|
|
117010
|
+
export: options.export
|
|
116607
117011
|
});
|
|
116608
117012
|
});
|
|
116609
117013
|
linear.command("my").description(
|
|
@@ -116850,6 +117254,101 @@ Examples:
|
|
|
116850
117254
|
});
|
|
116851
117255
|
}
|
|
116852
117256
|
|
|
117257
|
+
// src/commands/list.ts
|
|
117258
|
+
init_esm_shims();
|
|
117259
|
+
import { existsSync as existsSync16, readFileSync as readFileSync13, readdirSync, statSync } from "fs";
|
|
117260
|
+
import { join as join14 } from "path";
|
|
117261
|
+
function discoverSkills(skillsDir) {
|
|
117262
|
+
if (!existsSync16(skillsDir)) {
|
|
117263
|
+
return [];
|
|
117264
|
+
}
|
|
117265
|
+
const skills = [];
|
|
117266
|
+
try {
|
|
117267
|
+
const entries = readdirSync(skillsDir);
|
|
117268
|
+
for (const entry of entries) {
|
|
117269
|
+
const entryPath = join14(skillsDir, entry);
|
|
117270
|
+
const stat = statSync(entryPath);
|
|
117271
|
+
if (!stat.isDirectory()) continue;
|
|
117272
|
+
const skillPath = join14(entryPath, "SKILL.md");
|
|
117273
|
+
if (!existsSync16(skillPath)) continue;
|
|
117274
|
+
const content = readFileSync13(skillPath, "utf8");
|
|
117275
|
+
const description = extractDescription(content);
|
|
117276
|
+
skills.push({
|
|
117277
|
+
name: entry,
|
|
117278
|
+
description,
|
|
117279
|
+
path: skillPath
|
|
117280
|
+
});
|
|
117281
|
+
}
|
|
117282
|
+
} catch {
|
|
117283
|
+
return [];
|
|
117284
|
+
}
|
|
117285
|
+
return skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
117286
|
+
}
|
|
117287
|
+
function extractDescription(markdown) {
|
|
117288
|
+
const lines = markdown.split("\n");
|
|
117289
|
+
let inFrontmatter = false;
|
|
117290
|
+
let foundHeading = false;
|
|
117291
|
+
const descriptionLines = [];
|
|
117292
|
+
for (const line of lines) {
|
|
117293
|
+
const trimmed = line.trim();
|
|
117294
|
+
if (trimmed === "---") {
|
|
117295
|
+
if (!inFrontmatter && !foundHeading) {
|
|
117296
|
+
inFrontmatter = true;
|
|
117297
|
+
continue;
|
|
117298
|
+
} else if (inFrontmatter) {
|
|
117299
|
+
inFrontmatter = false;
|
|
117300
|
+
continue;
|
|
117301
|
+
}
|
|
117302
|
+
}
|
|
117303
|
+
if (inFrontmatter) continue;
|
|
117304
|
+
if (trimmed.startsWith("# ")) {
|
|
117305
|
+
foundHeading = true;
|
|
117306
|
+
continue;
|
|
117307
|
+
}
|
|
117308
|
+
if (foundHeading && trimmed.startsWith("#")) {
|
|
117309
|
+
break;
|
|
117310
|
+
}
|
|
117311
|
+
if (foundHeading) {
|
|
117312
|
+
if (trimmed === "") {
|
|
117313
|
+
if (descriptionLines.length > 0) {
|
|
117314
|
+
break;
|
|
117315
|
+
}
|
|
117316
|
+
continue;
|
|
117317
|
+
}
|
|
117318
|
+
descriptionLines.push(trimmed);
|
|
117319
|
+
}
|
|
117320
|
+
}
|
|
117321
|
+
return descriptionLines.join(" ").trim();
|
|
117322
|
+
}
|
|
117323
|
+
async function listAction(options, command) {
|
|
117324
|
+
const ctx = await createContext({
|
|
117325
|
+
format: options.json ? "json" : command.optsWithGlobals().format,
|
|
117326
|
+
verbose: command.optsWithGlobals().verbose,
|
|
117327
|
+
quiet: command.optsWithGlobals().quiet
|
|
117328
|
+
});
|
|
117329
|
+
const skillsDir = join14(process.cwd(), ".claude", "skills");
|
|
117330
|
+
const skills = discoverSkills(skillsDir);
|
|
117331
|
+
if (options.json) {
|
|
117332
|
+
ctx.output.data({ skills });
|
|
117333
|
+
return;
|
|
117334
|
+
}
|
|
117335
|
+
if (skills.length === 0) {
|
|
117336
|
+
ctx.output.message("No skills found in .claude/skills/");
|
|
117337
|
+
return;
|
|
117338
|
+
}
|
|
117339
|
+
ctx.output.data(`
|
|
117340
|
+
\u{1F4DA} Available Skills (${skills.length})`);
|
|
117341
|
+
ctx.output.data("\u2500".repeat(80));
|
|
117342
|
+
const rows = skills.map((skill) => ({
|
|
117343
|
+
Name: skill.name,
|
|
117344
|
+
Description: skill.description || "(no description)"
|
|
117345
|
+
}));
|
|
117346
|
+
ctx.output.table(rows);
|
|
117347
|
+
}
|
|
117348
|
+
function registerListCommand(program3) {
|
|
117349
|
+
program3.command("list").description("List available CLI skills for programmatic discovery").option("--json", "Output as JSON").action(listAction);
|
|
117350
|
+
}
|
|
117351
|
+
|
|
116853
117352
|
// src/commands/memory/index.ts
|
|
116854
117353
|
init_esm_shims();
|
|
116855
117354
|
|
|
@@ -117979,16 +118478,16 @@ function registerPipelineCommands(program3) {
|
|
|
117979
118478
|
// src/commands/plugin-sync.ts
|
|
117980
118479
|
init_esm_shims();
|
|
117981
118480
|
import { homedir as homedir3 } from "os";
|
|
117982
|
-
import { dirname as dirname3, join as
|
|
118481
|
+
import { dirname as dirname3, join as join15, resolve as resolve6 } from "path";
|
|
117983
118482
|
import { fileURLToPath } from "url";
|
|
117984
118483
|
var PLUGIN_SOURCE_DIR = resolve6(
|
|
117985
118484
|
dirname3(fileURLToPath(import.meta.url)),
|
|
117986
118485
|
"../../plugin"
|
|
117987
118486
|
);
|
|
117988
|
-
var PLUGIN_MANIFEST_RELATIVE =
|
|
118487
|
+
var PLUGIN_MANIFEST_RELATIVE = join15(".claude-plugin", "plugin.json");
|
|
117989
118488
|
var resolveTargetDir = (global2) => {
|
|
117990
|
-
const base =
|
|
117991
|
-
return
|
|
118489
|
+
const base = join15(homedir3(), ".claude", global2 ? "skills" : "plugins");
|
|
118490
|
+
return join15(base, "skill-cli");
|
|
117992
118491
|
};
|
|
117993
118492
|
var readManifest = async (path) => {
|
|
117994
118493
|
const manifest = await readJson(path);
|
|
@@ -118017,7 +118516,7 @@ var writeResult4 = (ctx, payload) => {
|
|
|
118017
118516
|
};
|
|
118018
118517
|
async function executePluginSync(ctx, options) {
|
|
118019
118518
|
try {
|
|
118020
|
-
const sourceManifestPath =
|
|
118519
|
+
const sourceManifestPath = join15(PLUGIN_SOURCE_DIR, PLUGIN_MANIFEST_RELATIVE);
|
|
118021
118520
|
const sourceExists = await pathExists(sourceManifestPath);
|
|
118022
118521
|
if (!sourceExists) {
|
|
118023
118522
|
throw new CLIError({
|
|
@@ -118027,7 +118526,7 @@ async function executePluginSync(ctx, options) {
|
|
|
118027
118526
|
}
|
|
118028
118527
|
const sourceManifest = await readManifest(sourceManifestPath);
|
|
118029
118528
|
const targetDir = resolveTargetDir(options.global);
|
|
118030
|
-
const targetManifestPath =
|
|
118529
|
+
const targetManifestPath = join15(targetDir, PLUGIN_MANIFEST_RELATIVE);
|
|
118031
118530
|
const targetExists = await pathExists(targetManifestPath);
|
|
118032
118531
|
if (targetExists && !options.force) {
|
|
118033
118532
|
const targetManifest = await readManifest(targetManifestPath);
|
|
@@ -119270,7 +119769,7 @@ init_esm_shims();
|
|
|
119270
119769
|
import { spawn } from "child_process";
|
|
119271
119770
|
import { writeFile as writeFile7 } from "fs/promises";
|
|
119272
119771
|
import { homedir as homedir4 } from "os";
|
|
119273
|
-
import { dirname as dirname4, join as
|
|
119772
|
+
import { dirname as dirname4, join as join16 } from "path";
|
|
119274
119773
|
var CONFIG_DIR_NAME = "skill-cli";
|
|
119275
119774
|
var AUTO_UPDATE_STATE_FILE = "auto-update.json";
|
|
119276
119775
|
var DEFAULT_PACKAGE = "@skillrecordings/cli";
|
|
@@ -119282,7 +119781,7 @@ var AutoUpdateStore = class {
|
|
|
119282
119781
|
now;
|
|
119283
119782
|
constructor(options = {}) {
|
|
119284
119783
|
const configDir = resolveConfigDir(options.configDir);
|
|
119285
|
-
this.filePath =
|
|
119784
|
+
this.filePath = join16(configDir, AUTO_UPDATE_STATE_FILE);
|
|
119286
119785
|
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
119287
119786
|
}
|
|
119288
119787
|
getNow() {
|
|
@@ -119310,9 +119809,9 @@ function resolveConfigDir(configDir) {
|
|
|
119310
119809
|
if (configDir) return configDir;
|
|
119311
119810
|
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
119312
119811
|
if (xdgConfigHome && xdgConfigHome.trim() !== "") {
|
|
119313
|
-
return
|
|
119812
|
+
return join16(xdgConfigHome, CONFIG_DIR_NAME);
|
|
119314
119813
|
}
|
|
119315
|
-
return
|
|
119814
|
+
return join16(homedir4(), ".config", CONFIG_DIR_NAME);
|
|
119316
119815
|
}
|
|
119317
119816
|
function isAutoUpdateState(value) {
|
|
119318
119817
|
if (!value || typeof value !== "object") return false;
|
|
@@ -119609,13 +120108,13 @@ var writeHints = (hints, stderr) => {
|
|
|
119609
120108
|
init_esm_shims();
|
|
119610
120109
|
import { lstat, readlink, symlink } from "fs/promises";
|
|
119611
120110
|
import { homedir as homedir5 } from "os";
|
|
119612
|
-
import { dirname as dirname5, join as
|
|
120111
|
+
import { dirname as dirname5, join as join17, resolve as resolve7 } from "path";
|
|
119613
120112
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
119614
120113
|
var SKILL_SOURCE_DIR = resolve7(
|
|
119615
120114
|
dirname5(fileURLToPath2(import.meta.url)),
|
|
119616
120115
|
"../../../../.claude/skills/skill-cli"
|
|
119617
120116
|
);
|
|
119618
|
-
var SKILL_TARGET_DIR =
|
|
120117
|
+
var SKILL_TARGET_DIR = join17(homedir5(), ".claude", "skills", "skill-cli");
|
|
119619
120118
|
async function autoLinkSkill() {
|
|
119620
120119
|
const source = SKILL_SOURCE_DIR;
|
|
119621
120120
|
const target = SKILL_TARGET_DIR;
|
|
@@ -119719,16 +120218,16 @@ async function sendTelemetryEvent(event) {
|
|
|
119719
120218
|
init_esm_shims();
|
|
119720
120219
|
import { writeFile as writeFile8 } from "fs/promises";
|
|
119721
120220
|
import { homedir as homedir6 } from "os";
|
|
119722
|
-
import { dirname as dirname6, join as
|
|
120221
|
+
import { dirname as dirname6, join as join18 } from "path";
|
|
119723
120222
|
var CONFIG_DIR_NAME2 = "skill-cli";
|
|
119724
120223
|
var USAGE_FILE_NAME = "usage.json";
|
|
119725
120224
|
function resolveConfigDir2(configDir) {
|
|
119726
120225
|
if (configDir) return configDir;
|
|
119727
120226
|
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
119728
120227
|
if (xdgConfigHome && xdgConfigHome.trim() !== "") {
|
|
119729
|
-
return
|
|
120228
|
+
return join18(xdgConfigHome, CONFIG_DIR_NAME2);
|
|
119730
120229
|
}
|
|
119731
|
-
return
|
|
120230
|
+
return join18(homedir6(), ".config", CONFIG_DIR_NAME2);
|
|
119732
120231
|
}
|
|
119733
120232
|
function createDefaultState(now) {
|
|
119734
120233
|
return {
|
|
@@ -119766,7 +120265,7 @@ var UsageTracker = class {
|
|
|
119766
120265
|
statePromise;
|
|
119767
120266
|
constructor(options = {}) {
|
|
119768
120267
|
const configDir = resolveConfigDir2(options.configDir);
|
|
119769
|
-
this.filePath =
|
|
120268
|
+
this.filePath = join18(configDir, USAGE_FILE_NAME);
|
|
119770
120269
|
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
119771
120270
|
}
|
|
119772
120271
|
async loadState() {
|
|
@@ -120345,8 +120844,8 @@ if (!envLoaded && !process.env.DATABASE_URL) {
|
|
|
120345
120844
|
process.env.SKIP_ENV_VALIDATION = "1";
|
|
120346
120845
|
}
|
|
120347
120846
|
var runtimeTarget = `bun-${process.platform}-${process.arch}`;
|
|
120348
|
-
var buildVersion = "0.
|
|
120349
|
-
var buildCommit = "
|
|
120847
|
+
var buildVersion = "0.18.0".length > 0 ? "0.18.0" : "0.0.0-dev";
|
|
120848
|
+
var buildCommit = "8d98b14".length > 0 ? "8d98b14" : "dev";
|
|
120350
120849
|
var buildTarget = "node".length > 0 ? "node" : runtimeTarget;
|
|
120351
120850
|
var isDevBuild = buildVersion.includes("dev") || buildCommit === "dev";
|
|
120352
120851
|
var versionLabel = `skill v${buildVersion} (${buildCommit}) ${buildTarget}`;
|
|
@@ -120551,6 +121050,8 @@ registerKbCommands(program2);
|
|
|
120551
121050
|
registerAuthCommands(program2, usageState);
|
|
120552
121051
|
registerConfigCommands(program2);
|
|
120553
121052
|
registerKeysCommands(program2);
|
|
121053
|
+
registerDoctorCommand(program2);
|
|
121054
|
+
registerListCommand(program2);
|
|
120554
121055
|
registerPluginSyncCommand(program2);
|
|
120555
121056
|
program2.command("mcp").description(
|
|
120556
121057
|
"Start MCP server for AI coding agent integration.\n Exposes 9 Front tools over JSON-RPC stdio for Claude Code, Cursor, etc.\n Tools: inbox, conversation, message, assign, reply, tag, archive, search, report\n Usage: skill mcp (then connect your AI editor to stdin/stdout)"
|