@meltstudio/meltctl 5.8.0 → 5.10.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 +365 -144
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7450,7 +7450,7 @@ var CLI_VERSION;
|
|
|
7450
7450
|
var init_version = __esm({
|
|
7451
7451
|
"src/utils/version.ts"() {
|
|
7452
7452
|
"use strict";
|
|
7453
|
-
CLI_VERSION = "5.
|
|
7453
|
+
CLI_VERSION = "5.10.0";
|
|
7454
7454
|
}
|
|
7455
7455
|
});
|
|
7456
7456
|
|
|
@@ -27935,6 +27935,32 @@ function createProfileAuditsResource(config2) {
|
|
|
27935
27935
|
};
|
|
27936
27936
|
}
|
|
27937
27937
|
|
|
27938
|
+
// ../sdk/dist/resources/support-effort.js
|
|
27939
|
+
function createSupportEffortResource(config2) {
|
|
27940
|
+
return {
|
|
27941
|
+
/**
|
|
27942
|
+
* Get the by-project + by-architect rollups, the week/month trend, and the
|
|
27943
|
+
* actionable missing-time worklist. `period` selects the trend granularity
|
|
27944
|
+
* (default 'week').
|
|
27945
|
+
*/
|
|
27946
|
+
async get(opts = {}) {
|
|
27947
|
+
const params = new URLSearchParams();
|
|
27948
|
+
if (opts.period)
|
|
27949
|
+
params.set("period", opts.period);
|
|
27950
|
+
const query = params.toString();
|
|
27951
|
+
const path10 = query ? `/support-effort?${query}` : "/support-effort";
|
|
27952
|
+
const { data, status } = await apiFetch(config2, path10);
|
|
27953
|
+
if (status === 403)
|
|
27954
|
+
throw new Error("Access denied. Only Team Managers can view support effort.");
|
|
27955
|
+
if (status !== 200) {
|
|
27956
|
+
const msg = data && typeof data === "object" && "error" in data ? data.error : void 0;
|
|
27957
|
+
throw new Error(msg ?? `Failed to fetch support effort (${status})`);
|
|
27958
|
+
}
|
|
27959
|
+
return data;
|
|
27960
|
+
}
|
|
27961
|
+
};
|
|
27962
|
+
}
|
|
27963
|
+
|
|
27938
27964
|
// ../sdk/dist/client.js
|
|
27939
27965
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
27940
27966
|
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([502, 503, 504]);
|
|
@@ -28006,7 +28032,8 @@ function createMeltClient(config2) {
|
|
|
28006
28032
|
me: createMeResource(config2),
|
|
28007
28033
|
developers: createDevelopersResource(config2),
|
|
28008
28034
|
endpointChecks: createEndpointChecksResource(config2),
|
|
28009
|
-
profileAudits: createProfileAuditsResource(config2)
|
|
28035
|
+
profileAudits: createProfileAuditsResource(config2),
|
|
28036
|
+
supportEffort: createSupportEffortResource(config2)
|
|
28010
28037
|
};
|
|
28011
28038
|
}
|
|
28012
28039
|
|
|
@@ -42515,6 +42542,253 @@ async function logoutCommand() {
|
|
|
42515
42542
|
// src/index.ts
|
|
42516
42543
|
init_version_check();
|
|
42517
42544
|
|
|
42545
|
+
// src/utils/skills-sync.ts
|
|
42546
|
+
init_source();
|
|
42547
|
+
|
|
42548
|
+
// src/utils/debug.ts
|
|
42549
|
+
init_source();
|
|
42550
|
+
function debugLog(message) {
|
|
42551
|
+
if (process.env["MELTCTL_DEBUG"]) {
|
|
42552
|
+
console.error(source_default.dim(`[debug] ${message}`));
|
|
42553
|
+
}
|
|
42554
|
+
}
|
|
42555
|
+
|
|
42556
|
+
// src/commands/skills.ts
|
|
42557
|
+
init_source();
|
|
42558
|
+
var import_fs_extra3 = __toESM(require_lib(), 1);
|
|
42559
|
+
import { createHash } from "crypto";
|
|
42560
|
+
import os5 from "os";
|
|
42561
|
+
import path5 from "path";
|
|
42562
|
+
|
|
42563
|
+
// src/utils/api.ts
|
|
42564
|
+
init_source();
|
|
42565
|
+
async function getToken() {
|
|
42566
|
+
const envToken = process.env["MELTCTL_TOKEN"];
|
|
42567
|
+
if (envToken) {
|
|
42568
|
+
return envToken;
|
|
42569
|
+
}
|
|
42570
|
+
const auth = await getStoredAuth();
|
|
42571
|
+
if (!auth) {
|
|
42572
|
+
console.error(
|
|
42573
|
+
source_default.red(
|
|
42574
|
+
"Not authenticated. Run `npx @meltstudio/meltctl@latest login` or set MELTCTL_TOKEN for CI."
|
|
42575
|
+
)
|
|
42576
|
+
);
|
|
42577
|
+
process.exit(1);
|
|
42578
|
+
}
|
|
42579
|
+
if (new Date(auth.expiresAt) <= /* @__PURE__ */ new Date()) {
|
|
42580
|
+
console.error(
|
|
42581
|
+
source_default.red(
|
|
42582
|
+
"Session expired. Run `npx @meltstudio/meltctl@latest login` to re-authenticate, or set MELTCTL_TOKEN for CI."
|
|
42583
|
+
)
|
|
42584
|
+
);
|
|
42585
|
+
process.exit(1);
|
|
42586
|
+
}
|
|
42587
|
+
return auth.token;
|
|
42588
|
+
}
|
|
42589
|
+
async function getClient() {
|
|
42590
|
+
const token = await getToken();
|
|
42591
|
+
return createMeltClient({ baseUrl: API_BASE, token });
|
|
42592
|
+
}
|
|
42593
|
+
|
|
42594
|
+
// src/commands/skills.ts
|
|
42595
|
+
var MARKER_FILE = ".melt-skills.json";
|
|
42596
|
+
function getSkillsRoot() {
|
|
42597
|
+
return path5.join(os5.homedir(), ".claude", "skills");
|
|
42598
|
+
}
|
|
42599
|
+
function computeSkillHash(files) {
|
|
42600
|
+
const hash2 = createHash("sha256");
|
|
42601
|
+
for (const filePath of Object.keys(files).sort()) {
|
|
42602
|
+
hash2.update(filePath);
|
|
42603
|
+
hash2.update(files[filePath] ?? "");
|
|
42604
|
+
}
|
|
42605
|
+
return hash2.digest("hex").slice(0, 12);
|
|
42606
|
+
}
|
|
42607
|
+
function readMarker(skillsRoot) {
|
|
42608
|
+
const markerPath = path5.join(skillsRoot, MARKER_FILE);
|
|
42609
|
+
if (!import_fs_extra3.default.existsSync(markerPath)) return null;
|
|
42610
|
+
try {
|
|
42611
|
+
const raw = import_fs_extra3.default.readJsonSync(markerPath);
|
|
42612
|
+
const skills2 = Array.isArray(raw.skills) ? raw.skills.map((entry) => {
|
|
42613
|
+
if (typeof entry === "string") {
|
|
42614
|
+
return { name: entry, hash: "" };
|
|
42615
|
+
}
|
|
42616
|
+
if (entry && typeof entry === "object") {
|
|
42617
|
+
const e = entry;
|
|
42618
|
+
return {
|
|
42619
|
+
name: typeof e.name === "string" ? e.name : "",
|
|
42620
|
+
hash: typeof e.hash === "string" ? e.hash : ""
|
|
42621
|
+
};
|
|
42622
|
+
}
|
|
42623
|
+
return { name: "", hash: "" };
|
|
42624
|
+
}) : [];
|
|
42625
|
+
return {
|
|
42626
|
+
version: typeof raw.version === "string" ? raw.version : "",
|
|
42627
|
+
installedAt: typeof raw.installedAt === "string" ? raw.installedAt : "",
|
|
42628
|
+
lastCheckedAt: typeof raw.lastCheckedAt === "string" ? raw.lastCheckedAt : void 0,
|
|
42629
|
+
skills: skills2
|
|
42630
|
+
};
|
|
42631
|
+
} catch {
|
|
42632
|
+
return null;
|
|
42633
|
+
}
|
|
42634
|
+
}
|
|
42635
|
+
function computeDiff(previous, next) {
|
|
42636
|
+
const prevByName = new Map(previous.map((s) => [s.name, s]));
|
|
42637
|
+
const nextNames = new Set(next.map((s) => s.name));
|
|
42638
|
+
const added = [];
|
|
42639
|
+
const changed = [];
|
|
42640
|
+
for (const skill of next) {
|
|
42641
|
+
const prev = prevByName.get(skill.name);
|
|
42642
|
+
if (!prev) {
|
|
42643
|
+
added.push(skill.name);
|
|
42644
|
+
continue;
|
|
42645
|
+
}
|
|
42646
|
+
if (prev.hash && prev.hash !== skill.hash) {
|
|
42647
|
+
changed.push(skill.name);
|
|
42648
|
+
}
|
|
42649
|
+
}
|
|
42650
|
+
const removed = previous.filter((s) => s.name && !nextNames.has(s.name)).map((s) => s.name);
|
|
42651
|
+
return { added, changed, removed };
|
|
42652
|
+
}
|
|
42653
|
+
function applyDevSkills(manifest, skillsRoot) {
|
|
42654
|
+
const previous = readMarker(skillsRoot)?.skills ?? [];
|
|
42655
|
+
const nextEntries = manifest.skills.map((s) => ({
|
|
42656
|
+
name: s.name,
|
|
42657
|
+
hash: computeSkillHash(s.files)
|
|
42658
|
+
}));
|
|
42659
|
+
const diff = computeDiff(previous, nextEntries);
|
|
42660
|
+
for (const name of diff.removed) {
|
|
42661
|
+
import_fs_extra3.default.removeSync(path5.join(skillsRoot, name));
|
|
42662
|
+
}
|
|
42663
|
+
for (const skill of manifest.skills) {
|
|
42664
|
+
const dir = path5.join(skillsRoot, skill.name);
|
|
42665
|
+
import_fs_extra3.default.removeSync(dir);
|
|
42666
|
+
for (const [relPath, content] of Object.entries(skill.files)) {
|
|
42667
|
+
import_fs_extra3.default.outputFileSync(path5.join(dir, relPath), content);
|
|
42668
|
+
}
|
|
42669
|
+
}
|
|
42670
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
42671
|
+
const marker = {
|
|
42672
|
+
version: manifest.version,
|
|
42673
|
+
installedAt: now,
|
|
42674
|
+
lastCheckedAt: now,
|
|
42675
|
+
skills: nextEntries
|
|
42676
|
+
};
|
|
42677
|
+
import_fs_extra3.default.outputFileSync(path5.join(skillsRoot, MARKER_FILE), `${JSON.stringify(marker, null, 2)}
|
|
42678
|
+
`);
|
|
42679
|
+
return diff;
|
|
42680
|
+
}
|
|
42681
|
+
function touchMarkerLastChecked(skillsRoot) {
|
|
42682
|
+
const marker = readMarker(skillsRoot);
|
|
42683
|
+
if (!marker) return;
|
|
42684
|
+
marker.lastCheckedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
42685
|
+
import_fs_extra3.default.outputFileSync(path5.join(skillsRoot, MARKER_FILE), `${JSON.stringify(marker, null, 2)}
|
|
42686
|
+
`);
|
|
42687
|
+
}
|
|
42688
|
+
async function skillsInstallCommand(opts) {
|
|
42689
|
+
const client = await getClient();
|
|
42690
|
+
let manifest;
|
|
42691
|
+
try {
|
|
42692
|
+
manifest = await client.skills.getDevSkills();
|
|
42693
|
+
} catch (error48) {
|
|
42694
|
+
console.error(
|
|
42695
|
+
source_default.red(
|
|
42696
|
+
`Failed to fetch Melt dev skills: ${error48 instanceof Error ? error48.message : "Unknown error"}`
|
|
42697
|
+
)
|
|
42698
|
+
);
|
|
42699
|
+
process.exit(1);
|
|
42700
|
+
}
|
|
42701
|
+
const skillsRoot = getSkillsRoot();
|
|
42702
|
+
const existing = readMarker(skillsRoot);
|
|
42703
|
+
if (existing && existing.version === manifest.version && !opts.force) {
|
|
42704
|
+
console.log(
|
|
42705
|
+
source_default.dim(
|
|
42706
|
+
`
|
|
42707
|
+
Melt dev skills already up to date (v${manifest.version}). Use --force to reinstall.
|
|
42708
|
+
`
|
|
42709
|
+
)
|
|
42710
|
+
);
|
|
42711
|
+
return;
|
|
42712
|
+
}
|
|
42713
|
+
applyDevSkills(manifest, skillsRoot);
|
|
42714
|
+
console.log(
|
|
42715
|
+
source_default.green(
|
|
42716
|
+
`
|
|
42717
|
+
\u2713 Installed ${manifest.skills.length} Melt dev skills (v${manifest.version}) to ~/.claude/skills/.`
|
|
42718
|
+
)
|
|
42719
|
+
);
|
|
42720
|
+
console.log(
|
|
42721
|
+
source_default.dim(
|
|
42722
|
+
" Restart Claude Code to load them (they appear as /melt-dev-plan, /melt-dev-review, \u2026).\n"
|
|
42723
|
+
)
|
|
42724
|
+
);
|
|
42725
|
+
}
|
|
42726
|
+
|
|
42727
|
+
// src/utils/skills-sync.ts
|
|
42728
|
+
var TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
|
|
42729
|
+
var SKIP_SYNC_COMMANDS = /* @__PURE__ */ new Set(["skills", "login", "logout", "update", "version"]);
|
|
42730
|
+
function formatSyncNotice(oldVersion, newVersion, diff) {
|
|
42731
|
+
const groups = [];
|
|
42732
|
+
if (diff.changed.length > 0) groups.push(`changed ${diff.changed.join(", ")}`);
|
|
42733
|
+
if (diff.added.length > 0) groups.push(`added ${diff.added.join(", ")}`);
|
|
42734
|
+
if (diff.removed.length > 0) groups.push(`removed ${diff.removed.join(", ")}`);
|
|
42735
|
+
const suffix = groups.length > 0 ? `: ${groups.join("; ")}` : "";
|
|
42736
|
+
return `\u2191 Melt dev skills updated (v${oldVersion} \u2192 v${newVersion})${suffix}`;
|
|
42737
|
+
}
|
|
42738
|
+
function isCheckDue(lastCheckedAt, now) {
|
|
42739
|
+
if (!lastCheckedAt) return true;
|
|
42740
|
+
const last = Date.parse(lastCheckedAt);
|
|
42741
|
+
if (Number.isNaN(last)) return true;
|
|
42742
|
+
return now - last >= TWENTY_FOUR_HOURS_MS;
|
|
42743
|
+
}
|
|
42744
|
+
async function maybeSyncSkills(commandName) {
|
|
42745
|
+
try {
|
|
42746
|
+
if (process.env["MELTCTL_SKIP_SKILLS_SYNC"]) {
|
|
42747
|
+
debugLog("Skills sync disabled via MELTCTL_SKIP_SKILLS_SYNC");
|
|
42748
|
+
return;
|
|
42749
|
+
}
|
|
42750
|
+
if (commandName && SKIP_SYNC_COMMANDS.has(commandName)) {
|
|
42751
|
+
debugLog(`Skills sync skipped for command "${commandName}"`);
|
|
42752
|
+
return;
|
|
42753
|
+
}
|
|
42754
|
+
const auth = await getStoredAuth();
|
|
42755
|
+
if (!auth || new Date(auth.expiresAt) <= /* @__PURE__ */ new Date()) {
|
|
42756
|
+
debugLog("Skills sync skipped: not authenticated or token expired");
|
|
42757
|
+
return;
|
|
42758
|
+
}
|
|
42759
|
+
const skillsRoot = getSkillsRoot();
|
|
42760
|
+
const marker = readMarker(skillsRoot);
|
|
42761
|
+
if (!marker) {
|
|
42762
|
+
debugLog("Skills sync skipped: no skills marker (not installed)");
|
|
42763
|
+
return;
|
|
42764
|
+
}
|
|
42765
|
+
if (!isCheckDue(marker.lastCheckedAt, Date.now())) {
|
|
42766
|
+
debugLog("Skills sync skipped: checked within the last 24h");
|
|
42767
|
+
return;
|
|
42768
|
+
}
|
|
42769
|
+
const client = createMeltClient({ baseUrl: API_BASE, token: auth.token });
|
|
42770
|
+
let manifest;
|
|
42771
|
+
try {
|
|
42772
|
+
manifest = await client.skills.getDevSkills();
|
|
42773
|
+
} catch (e) {
|
|
42774
|
+
touchMarkerLastChecked(skillsRoot);
|
|
42775
|
+
debugLog(`Skills sync fetch error: ${e instanceof Error ? e.message : e}`);
|
|
42776
|
+
return;
|
|
42777
|
+
}
|
|
42778
|
+
if (manifest.version === marker.version) {
|
|
42779
|
+
touchMarkerLastChecked(skillsRoot);
|
|
42780
|
+
debugLog("Skills sync: already up to date");
|
|
42781
|
+
return;
|
|
42782
|
+
}
|
|
42783
|
+
const oldVersion = marker.version;
|
|
42784
|
+
const diff = applyDevSkills(manifest, skillsRoot);
|
|
42785
|
+
console.log(source_default.dim(source_default.green(formatSyncNotice(oldVersion, manifest.version, diff))));
|
|
42786
|
+
debugLog(`Skills sync: updated ${oldVersion} \u2192 ${manifest.version}`);
|
|
42787
|
+
} catch (e) {
|
|
42788
|
+
debugLog(`Skills sync error: ${e instanceof Error ? e.message : e}`);
|
|
42789
|
+
}
|
|
42790
|
+
}
|
|
42791
|
+
|
|
42518
42792
|
// src/commands/version.ts
|
|
42519
42793
|
init_source();
|
|
42520
42794
|
init_version_check();
|
|
@@ -42611,39 +42885,6 @@ async function versionCheckCommand() {
|
|
|
42611
42885
|
// src/commands/standup.ts
|
|
42612
42886
|
init_source();
|
|
42613
42887
|
init_dist11();
|
|
42614
|
-
|
|
42615
|
-
// src/utils/api.ts
|
|
42616
|
-
init_source();
|
|
42617
|
-
async function getToken() {
|
|
42618
|
-
const envToken = process.env["MELTCTL_TOKEN"];
|
|
42619
|
-
if (envToken) {
|
|
42620
|
-
return envToken;
|
|
42621
|
-
}
|
|
42622
|
-
const auth = await getStoredAuth();
|
|
42623
|
-
if (!auth) {
|
|
42624
|
-
console.error(
|
|
42625
|
-
source_default.red(
|
|
42626
|
-
"Not authenticated. Run `npx @meltstudio/meltctl@latest login` or set MELTCTL_TOKEN for CI."
|
|
42627
|
-
)
|
|
42628
|
-
);
|
|
42629
|
-
process.exit(1);
|
|
42630
|
-
}
|
|
42631
|
-
if (new Date(auth.expiresAt) <= /* @__PURE__ */ new Date()) {
|
|
42632
|
-
console.error(
|
|
42633
|
-
source_default.red(
|
|
42634
|
-
"Session expired. Run `npx @meltstudio/meltctl@latest login` to re-authenticate, or set MELTCTL_TOKEN for CI."
|
|
42635
|
-
)
|
|
42636
|
-
);
|
|
42637
|
-
process.exit(1);
|
|
42638
|
-
}
|
|
42639
|
-
return auth.token;
|
|
42640
|
-
}
|
|
42641
|
-
async function getClient() {
|
|
42642
|
-
const token = await getToken();
|
|
42643
|
-
return createMeltClient({ baseUrl: API_BASE, token });
|
|
42644
|
-
}
|
|
42645
|
-
|
|
42646
|
-
// src/commands/standup.ts
|
|
42647
42888
|
var EDITOR_HINT = source_default.dim("(type \\e to open your editor)");
|
|
42648
42889
|
async function promptField(message, required2) {
|
|
42649
42890
|
const value = await dist_default7({ message: `${message} ${EDITOR_HINT}` });
|
|
@@ -42865,12 +43106,12 @@ async function showLeaderboard() {
|
|
|
42865
43106
|
|
|
42866
43107
|
// src/commands/audit.ts
|
|
42867
43108
|
init_source();
|
|
42868
|
-
var
|
|
42869
|
-
import
|
|
43109
|
+
var import_fs_extra5 = __toESM(require_lib(), 1);
|
|
43110
|
+
import path7 from "path";
|
|
42870
43111
|
|
|
42871
43112
|
// src/utils/git.ts
|
|
42872
|
-
var
|
|
42873
|
-
import
|
|
43113
|
+
var import_fs_extra4 = __toESM(require_lib(), 1);
|
|
43114
|
+
import path6 from "path";
|
|
42874
43115
|
import { execSync as execSync4 } from "child_process";
|
|
42875
43116
|
function getGitBranch() {
|
|
42876
43117
|
try {
|
|
@@ -42908,34 +43149,34 @@ function getGitRepository() {
|
|
|
42908
43149
|
function getProjectName() {
|
|
42909
43150
|
const cwd = process.cwd();
|
|
42910
43151
|
try {
|
|
42911
|
-
const pkgPath =
|
|
42912
|
-
if (
|
|
42913
|
-
const pkg =
|
|
43152
|
+
const pkgPath = path6.join(cwd, "package.json");
|
|
43153
|
+
if (import_fs_extra4.default.pathExistsSync(pkgPath)) {
|
|
43154
|
+
const pkg = import_fs_extra4.default.readJsonSync(pkgPath);
|
|
42914
43155
|
if (pkg.name) {
|
|
42915
43156
|
return pkg.name;
|
|
42916
43157
|
}
|
|
42917
43158
|
}
|
|
42918
43159
|
} catch {
|
|
42919
43160
|
}
|
|
42920
|
-
return
|
|
43161
|
+
return path6.basename(cwd);
|
|
42921
43162
|
}
|
|
42922
43163
|
function extractTicketId(branch) {
|
|
42923
43164
|
const match = branch.match(/([A-Z]+-\d+)/i);
|
|
42924
43165
|
return match ? match[1] : null;
|
|
42925
43166
|
}
|
|
42926
43167
|
async function findMdFiles(dir) {
|
|
42927
|
-
if (!await
|
|
43168
|
+
if (!await import_fs_extra4.default.pathExists(dir)) {
|
|
42928
43169
|
return [];
|
|
42929
43170
|
}
|
|
42930
43171
|
const results = [];
|
|
42931
43172
|
async function walk(current) {
|
|
42932
|
-
const entries = await
|
|
43173
|
+
const entries = await import_fs_extra4.default.readdir(current, { withFileTypes: true });
|
|
42933
43174
|
for (const entry of entries) {
|
|
42934
|
-
const fullPath =
|
|
43175
|
+
const fullPath = path6.join(current, entry.name);
|
|
42935
43176
|
if (entry.isDirectory()) {
|
|
42936
43177
|
await walk(fullPath);
|
|
42937
43178
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
42938
|
-
const stat = await
|
|
43179
|
+
const stat = await import_fs_extra4.default.stat(fullPath);
|
|
42939
43180
|
results.push({ path: fullPath, mtime: stat.mtimeMs });
|
|
42940
43181
|
}
|
|
42941
43182
|
}
|
|
@@ -42954,15 +43195,15 @@ function detectAuditType(filename) {
|
|
|
42954
43195
|
}
|
|
42955
43196
|
async function autoDetectAuditFile() {
|
|
42956
43197
|
const cwd = process.cwd();
|
|
42957
|
-
const auditsDir =
|
|
43198
|
+
const auditsDir = path7.join(cwd, ".audits");
|
|
42958
43199
|
const auditFiles = await findMdFiles(auditsDir);
|
|
42959
43200
|
if (auditFiles.length > 0) {
|
|
42960
43201
|
return auditFiles[0] ?? null;
|
|
42961
43202
|
}
|
|
42962
43203
|
const candidates = ["AUDIT.md", "UX-AUDIT.md", "SECURITY-AUDIT.md"];
|
|
42963
43204
|
for (const name of candidates) {
|
|
42964
|
-
const filePath =
|
|
42965
|
-
if (await
|
|
43205
|
+
const filePath = path7.join(cwd, name);
|
|
43206
|
+
if (await import_fs_extra5.default.pathExists(filePath)) {
|
|
42966
43207
|
return filePath;
|
|
42967
43208
|
}
|
|
42968
43209
|
}
|
|
@@ -42972,7 +43213,7 @@ async function auditSubmitCommand(file2) {
|
|
|
42972
43213
|
const client = await getClient();
|
|
42973
43214
|
let filePath;
|
|
42974
43215
|
if (file2) {
|
|
42975
|
-
filePath =
|
|
43216
|
+
filePath = path7.resolve(file2);
|
|
42976
43217
|
} else {
|
|
42977
43218
|
const detected = await autoDetectAuditFile();
|
|
42978
43219
|
if (!detected) {
|
|
@@ -42984,14 +43225,14 @@ async function auditSubmitCommand(file2) {
|
|
|
42984
43225
|
process.exit(1);
|
|
42985
43226
|
}
|
|
42986
43227
|
filePath = detected;
|
|
42987
|
-
console.log(source_default.dim(`Auto-detected audit file: ${
|
|
43228
|
+
console.log(source_default.dim(`Auto-detected audit file: ${path7.relative(process.cwd(), filePath)}`));
|
|
42988
43229
|
}
|
|
42989
|
-
if (!await
|
|
43230
|
+
if (!await import_fs_extra5.default.pathExists(filePath)) {
|
|
42990
43231
|
console.error(source_default.red(`File not found: ${filePath}`));
|
|
42991
43232
|
process.exit(1);
|
|
42992
43233
|
}
|
|
42993
|
-
const content = await
|
|
42994
|
-
const filename =
|
|
43234
|
+
const content = await import_fs_extra5.default.readFile(filePath, "utf-8");
|
|
43235
|
+
const filename = path7.basename(filePath);
|
|
42995
43236
|
const auditType = detectAuditType(filename);
|
|
42996
43237
|
const project2 = getProjectName();
|
|
42997
43238
|
const branch = getGitBranch();
|
|
@@ -43116,11 +43357,11 @@ async function auditViewCommand(id, options) {
|
|
|
43116
43357
|
try {
|
|
43117
43358
|
const audit2 = await client.audits.get(id);
|
|
43118
43359
|
if (options.output) {
|
|
43119
|
-
const outputPath =
|
|
43120
|
-
await
|
|
43121
|
-
await
|
|
43360
|
+
const outputPath = path7.resolve(options.output);
|
|
43361
|
+
await import_fs_extra5.default.ensureDir(path7.dirname(outputPath));
|
|
43362
|
+
await import_fs_extra5.default.writeFile(outputPath, audit2.content, "utf-8");
|
|
43122
43363
|
console.log(source_default.green(`
|
|
43123
|
-
\u2713 Audit saved to ${
|
|
43364
|
+
\u2713 Audit saved to ${path7.relative(process.cwd(), outputPath)}
|
|
43124
43365
|
`));
|
|
43125
43366
|
} else {
|
|
43126
43367
|
const typeLabels = {
|
|
@@ -43152,8 +43393,8 @@ async function auditViewCommand(id, options) {
|
|
|
43152
43393
|
|
|
43153
43394
|
// src/commands/plan.ts
|
|
43154
43395
|
init_source();
|
|
43155
|
-
var
|
|
43156
|
-
import
|
|
43396
|
+
var import_fs_extra6 = __toESM(require_lib(), 1);
|
|
43397
|
+
import path8 from "path";
|
|
43157
43398
|
function extractFrontmatterStatus(content) {
|
|
43158
43399
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
43159
43400
|
if (!match) return null;
|
|
@@ -43162,7 +43403,7 @@ function extractFrontmatterStatus(content) {
|
|
|
43162
43403
|
}
|
|
43163
43404
|
async function autoDetectPlanFile() {
|
|
43164
43405
|
const cwd = process.cwd();
|
|
43165
|
-
const plansDir =
|
|
43406
|
+
const plansDir = path8.join(cwd, ".plans");
|
|
43166
43407
|
const mdFiles = await findMdFiles(plansDir);
|
|
43167
43408
|
if (mdFiles.length === 0) {
|
|
43168
43409
|
return null;
|
|
@@ -43171,7 +43412,7 @@ async function autoDetectPlanFile() {
|
|
|
43171
43412
|
const ticketId = extractTicketId(branch);
|
|
43172
43413
|
if (ticketId) {
|
|
43173
43414
|
const ticketLower = ticketId.toLowerCase();
|
|
43174
|
-
const match = mdFiles.find((f) =>
|
|
43415
|
+
const match = mdFiles.find((f) => path8.basename(f).toLowerCase().includes(ticketLower));
|
|
43175
43416
|
if (match) {
|
|
43176
43417
|
return match;
|
|
43177
43418
|
}
|
|
@@ -43182,7 +43423,7 @@ async function planSubmitCommand(file2) {
|
|
|
43182
43423
|
const client = await getClient();
|
|
43183
43424
|
let filePath;
|
|
43184
43425
|
if (file2) {
|
|
43185
|
-
filePath =
|
|
43426
|
+
filePath = path8.resolve(file2);
|
|
43186
43427
|
} else {
|
|
43187
43428
|
const detected = await autoDetectPlanFile();
|
|
43188
43429
|
if (!detected) {
|
|
@@ -43194,14 +43435,14 @@ async function planSubmitCommand(file2) {
|
|
|
43194
43435
|
process.exit(1);
|
|
43195
43436
|
}
|
|
43196
43437
|
filePath = detected;
|
|
43197
|
-
console.log(source_default.dim(`Auto-detected plan file: ${
|
|
43438
|
+
console.log(source_default.dim(`Auto-detected plan file: ${path8.relative(process.cwd(), filePath)}`));
|
|
43198
43439
|
}
|
|
43199
|
-
if (!await
|
|
43440
|
+
if (!await import_fs_extra6.default.pathExists(filePath)) {
|
|
43200
43441
|
console.error(source_default.red(`File not found: ${filePath}`));
|
|
43201
43442
|
process.exit(1);
|
|
43202
43443
|
}
|
|
43203
|
-
const content = await
|
|
43204
|
-
const filename =
|
|
43444
|
+
const content = await import_fs_extra6.default.readFile(filePath, "utf-8");
|
|
43445
|
+
const filename = path8.basename(filePath);
|
|
43205
43446
|
const project2 = getProjectName();
|
|
43206
43447
|
const branch = getGitBranch();
|
|
43207
43448
|
const commit = getGitCommit();
|
|
@@ -43288,84 +43529,9 @@ async function planListCommand(options) {
|
|
|
43288
43529
|
}
|
|
43289
43530
|
}
|
|
43290
43531
|
|
|
43291
|
-
// src/commands/skills.ts
|
|
43292
|
-
init_source();
|
|
43293
|
-
var import_fs_extra6 = __toESM(require_lib(), 1);
|
|
43294
|
-
import os5 from "os";
|
|
43295
|
-
import path8 from "path";
|
|
43296
|
-
var MARKER_FILE = ".melt-skills.json";
|
|
43297
|
-
function readMarker(skillsRoot) {
|
|
43298
|
-
const markerPath = path8.join(skillsRoot, MARKER_FILE);
|
|
43299
|
-
if (!import_fs_extra6.default.existsSync(markerPath)) return null;
|
|
43300
|
-
try {
|
|
43301
|
-
return import_fs_extra6.default.readJsonSync(markerPath);
|
|
43302
|
-
} catch {
|
|
43303
|
-
return null;
|
|
43304
|
-
}
|
|
43305
|
-
}
|
|
43306
|
-
async function skillsInstallCommand(opts) {
|
|
43307
|
-
const client = await getClient();
|
|
43308
|
-
let manifest;
|
|
43309
|
-
try {
|
|
43310
|
-
manifest = await client.skills.getDevSkills();
|
|
43311
|
-
} catch (error48) {
|
|
43312
|
-
console.error(
|
|
43313
|
-
source_default.red(
|
|
43314
|
-
`Failed to fetch Melt dev skills: ${error48 instanceof Error ? error48.message : "Unknown error"}`
|
|
43315
|
-
)
|
|
43316
|
-
);
|
|
43317
|
-
process.exit(1);
|
|
43318
|
-
}
|
|
43319
|
-
const skillsRoot = path8.join(os5.homedir(), ".claude", "skills");
|
|
43320
|
-
const existing = readMarker(skillsRoot);
|
|
43321
|
-
if (existing && existing.version === manifest.version && !opts.force) {
|
|
43322
|
-
console.log(
|
|
43323
|
-
source_default.dim(
|
|
43324
|
-
`
|
|
43325
|
-
Melt dev skills already up to date (v${manifest.version}). Use --force to reinstall.
|
|
43326
|
-
`
|
|
43327
|
-
)
|
|
43328
|
-
);
|
|
43329
|
-
return;
|
|
43330
|
-
}
|
|
43331
|
-
for (const skill of manifest.skills) {
|
|
43332
|
-
const dir = path8.join(skillsRoot, skill.name);
|
|
43333
|
-
import_fs_extra6.default.removeSync(dir);
|
|
43334
|
-
for (const [relPath, content] of Object.entries(skill.files)) {
|
|
43335
|
-
import_fs_extra6.default.outputFileSync(path8.join(dir, relPath), content);
|
|
43336
|
-
}
|
|
43337
|
-
}
|
|
43338
|
-
const marker = {
|
|
43339
|
-
version: manifest.version,
|
|
43340
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
43341
|
-
skills: manifest.skills.map((s) => s.name)
|
|
43342
|
-
};
|
|
43343
|
-
import_fs_extra6.default.outputFileSync(path8.join(skillsRoot, MARKER_FILE), `${JSON.stringify(marker, null, 2)}
|
|
43344
|
-
`);
|
|
43345
|
-
console.log(
|
|
43346
|
-
source_default.green(
|
|
43347
|
-
`
|
|
43348
|
-
\u2713 Installed ${manifest.skills.length} Melt dev skills (v${manifest.version}) to ~/.claude/skills/.`
|
|
43349
|
-
)
|
|
43350
|
-
);
|
|
43351
|
-
console.log(
|
|
43352
|
-
source_default.dim(
|
|
43353
|
-
" Restart Claude Code to load them (they appear as /melt-dev-plan, /melt-dev-review, \u2026).\n"
|
|
43354
|
-
)
|
|
43355
|
-
);
|
|
43356
|
-
}
|
|
43357
|
-
|
|
43358
43532
|
// src/index.ts
|
|
43359
43533
|
init_update();
|
|
43360
43534
|
|
|
43361
|
-
// src/utils/debug.ts
|
|
43362
|
-
init_source();
|
|
43363
|
-
function debugLog(message) {
|
|
43364
|
-
if (process.env["MELTCTL_DEBUG"]) {
|
|
43365
|
-
console.error(source_default.dim(`[debug] ${message}`));
|
|
43366
|
-
}
|
|
43367
|
-
}
|
|
43368
|
-
|
|
43369
43535
|
// src/utils/analytics.ts
|
|
43370
43536
|
init_version();
|
|
43371
43537
|
async function trackCommand(command, success2, errorMessage) {
|
|
@@ -54357,6 +54523,30 @@ function createProfileAuditsResource2(config2) {
|
|
|
54357
54523
|
}
|
|
54358
54524
|
};
|
|
54359
54525
|
}
|
|
54526
|
+
function createSupportEffortResource2(config2) {
|
|
54527
|
+
return {
|
|
54528
|
+
/**
|
|
54529
|
+
* Get the by-project + by-architect rollups, the week/month trend, and the
|
|
54530
|
+
* actionable missing-time worklist. `period` selects the trend granularity
|
|
54531
|
+
* (default 'week').
|
|
54532
|
+
*/
|
|
54533
|
+
async get(opts = {}) {
|
|
54534
|
+
const params = new URLSearchParams();
|
|
54535
|
+
if (opts.period)
|
|
54536
|
+
params.set("period", opts.period);
|
|
54537
|
+
const query = params.toString();
|
|
54538
|
+
const path22 = query ? `/support-effort?${query}` : "/support-effort";
|
|
54539
|
+
const { data, status } = await apiFetch2(config2, path22);
|
|
54540
|
+
if (status === 403)
|
|
54541
|
+
throw new Error("Access denied. Only Team Managers can view support effort.");
|
|
54542
|
+
if (status !== 200) {
|
|
54543
|
+
const msg = data && typeof data === "object" && "error" in data ? data.error : void 0;
|
|
54544
|
+
throw new Error(msg ?? `Failed to fetch support effort (${status})`);
|
|
54545
|
+
}
|
|
54546
|
+
return data;
|
|
54547
|
+
}
|
|
54548
|
+
};
|
|
54549
|
+
}
|
|
54360
54550
|
var DEFAULT_TIMEOUT_MS2 = 1e4;
|
|
54361
54551
|
var RETRYABLE_STATUS2 = /* @__PURE__ */ new Set([502, 503, 504]);
|
|
54362
54552
|
function delay2(ms) {
|
|
@@ -54427,7 +54617,8 @@ function createMeltClient2(config2) {
|
|
|
54427
54617
|
me: createMeResource2(config2),
|
|
54428
54618
|
developers: createDevelopersResource2(config2),
|
|
54429
54619
|
endpointChecks: createEndpointChecksResource2(config2),
|
|
54430
|
-
profileAudits: createProfileAuditsResource2(config2)
|
|
54620
|
+
profileAudits: createProfileAuditsResource2(config2),
|
|
54621
|
+
supportEffort: createSupportEffortResource2(config2)
|
|
54431
54622
|
};
|
|
54432
54623
|
}
|
|
54433
54624
|
var auditFindingSchema2 = external_exports.object({
|
|
@@ -55694,6 +55885,34 @@ function registerEndpointCheckTools(server, getClient2) {
|
|
|
55694
55885
|
withClientArgs(getClient2, submitEndpointCheck)
|
|
55695
55886
|
);
|
|
55696
55887
|
}
|
|
55888
|
+
async function getSupportEffort(client, input) {
|
|
55889
|
+
return safe(async () => {
|
|
55890
|
+
const data = await client.supportEffort.get({ period: input.period });
|
|
55891
|
+
const totalMissingTime = data.missingTimeTickets.length;
|
|
55892
|
+
return {
|
|
55893
|
+
asOf: data.asOf,
|
|
55894
|
+
byProject: data.byProject,
|
|
55895
|
+
byArchitect: data.byArchitect,
|
|
55896
|
+
trend: data.trend,
|
|
55897
|
+
// Surface the count prominently; the full actionable list is available via
|
|
55898
|
+
// the read API / dashboard page (Slice 2) rather than flooding the tool.
|
|
55899
|
+
missingTimeCount: totalMissingTime
|
|
55900
|
+
};
|
|
55901
|
+
});
|
|
55902
|
+
}
|
|
55903
|
+
function registerSupportEffortTools(server, getClient2) {
|
|
55904
|
+
server.registerTool(
|
|
55905
|
+
"get_support_effort",
|
|
55906
|
+
{
|
|
55907
|
+
title: "Get tech-support effort",
|
|
55908
|
+
description: "Tech-support / DevOps effort rollup for the 'Tech Support Ops' Linear team (key DEV). Returns logged hours (parsed from each ticket's self-reported 'Time Spent' label \u2014 the architect's active effort, NOT cycle time) and ticket counts rolled up two ways: `byProject` (which client/product the support load lands on) and `byArchitect` (who is carrying it). Each rollup entry has ticketCount, totalLoggedMinutes, avgLoggedMinutes, and missingTimeCount. Also returns `missingTimeCount`: the number of Done tickets with no time label (a data-quality gap that makes the hours less trustworthy \u2014 the dashboard page surfaces the actionable list). `period` selects the trend granularity (week|month, default week). Manager-gated. Use for 'how much support work did <project> need?', 'who's carrying the support load?', and 'is our support time logged completely?'.",
|
|
55909
|
+
inputSchema: {
|
|
55910
|
+
period: external_exports.enum(["week", "month"]).optional().describe("Trend bucket granularity. Default week.")
|
|
55911
|
+
}
|
|
55912
|
+
},
|
|
55913
|
+
withClientArgs(getClient2, getSupportEffort)
|
|
55914
|
+
);
|
|
55915
|
+
}
|
|
55697
55916
|
var VERSION = "0.0.0";
|
|
55698
55917
|
function createMcpServer(clientOrProvider) {
|
|
55699
55918
|
const getClient2 = typeof clientOrProvider === "function" ? clientOrProvider : () => Promise.resolve(clientOrProvider);
|
|
@@ -55712,6 +55931,7 @@ function createMcpServer(clientOrProvider) {
|
|
|
55712
55931
|
registerFindingsTools(server, getClient2);
|
|
55713
55932
|
registerDailyPlanTools(server, getClient2);
|
|
55714
55933
|
registerEndpointCheckTools(server, getClient2);
|
|
55934
|
+
registerSupportEffortTools(server, getClient2);
|
|
55715
55935
|
return server;
|
|
55716
55936
|
}
|
|
55717
55937
|
async function startServer() {
|
|
@@ -55777,6 +55997,7 @@ ${source_default.dim(" event track --skill Track skill usage (called by skil
|
|
|
55777
55997
|
cmd = cmd.parent;
|
|
55778
55998
|
}
|
|
55779
55999
|
await checkAndEnforceUpdate(cmd.name());
|
|
56000
|
+
await maybeSyncSkills(cmd.name());
|
|
55780
56001
|
}).hook("postAction", async (_thisCommand, actionCommand) => {
|
|
55781
56002
|
try {
|
|
55782
56003
|
const parts = [];
|
package/package.json
CHANGED