@meltstudio/meltctl 5.22.0 → 5.24.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.
Files changed (2) hide show
  1. package/dist/index.js +150 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7499,7 +7499,7 @@ var CLI_VERSION;
7499
7499
  var init_version = __esm({
7500
7500
  "src/utils/version.ts"() {
7501
7501
  "use strict";
7502
- CLI_VERSION = "5.22.0";
7502
+ CLI_VERSION = "5.24.0";
7503
7503
  }
7504
7504
  });
7505
7505
 
@@ -27010,6 +27010,8 @@ function createProjectsResource(config2) {
27010
27010
  method: "POST",
27011
27011
  body: JSON.stringify(input)
27012
27012
  });
27013
+ if (status === 403)
27014
+ throw new Error("Access denied. Only Team Managers can link repos.");
27013
27015
  if (status !== 201)
27014
27016
  throw new Error(`Failed to add repo (${status})`);
27015
27017
  },
@@ -27018,6 +27020,8 @@ function createProjectsResource(config2) {
27018
27020
  method: "DELETE",
27019
27021
  body: JSON.stringify({ repository })
27020
27022
  });
27023
+ if (status === 403)
27024
+ throw new Error("Access denied. Only Team Managers can link repos.");
27021
27025
  if (status !== 200)
27022
27026
  throw new Error(`Failed to remove repo (${status})`);
27023
27027
  },
@@ -27953,6 +27957,39 @@ function createEndpointChecksResource(config2) {
27953
27957
  if (status >= 400) {
27954
27958
  throw new Error(data.error ?? `Failed to delete endpoint check (${status})`);
27955
27959
  }
27960
+ },
27961
+ /** Weekly Slack reminder settings (#651). Manager-gated. */
27962
+ async getReminderSettings() {
27963
+ const { data, status } = await apiFetch(config2, "/endpoint-reminders/settings");
27964
+ if (status === 403) {
27965
+ throw new Error("Access denied. Only Team Managers can view endpoint reminder settings.");
27966
+ }
27967
+ if (status !== 200) {
27968
+ throw new Error(data.error ?? `Failed to fetch endpoint reminder settings (${status})`);
27969
+ }
27970
+ return data.settings;
27971
+ },
27972
+ /** Save the reminder settings (#651). Turning `enabled` on starts the weekly DMs. */
27973
+ async updateReminderSettings(input) {
27974
+ const { data, status } = await apiFetch(config2, "/endpoint-reminders/settings", { method: "PUT", body: JSON.stringify(input) });
27975
+ if (status === 403) {
27976
+ throw new Error("Access denied. Only Team Managers can change endpoint reminder settings.");
27977
+ }
27978
+ if (status !== 200) {
27979
+ throw new Error(data.error ?? `Failed to save endpoint reminder settings (${status})`);
27980
+ }
27981
+ return data.settings;
27982
+ },
27983
+ /** Who would get a DM this week and who would be in the digest. Sends nothing. */
27984
+ async previewReminders() {
27985
+ const { data, status } = await apiFetch(config2, "/endpoint-reminders/preview");
27986
+ if (status === 403) {
27987
+ throw new Error("Access denied. Only Team Managers can preview endpoint reminders.");
27988
+ }
27989
+ if (status !== 200) {
27990
+ throw new Error(data.error ?? `Failed to preview endpoint reminders (${status})`);
27991
+ }
27992
+ return data;
27956
27993
  }
27957
27994
  };
27958
27995
  }
@@ -54207,6 +54244,8 @@ function createProjectsResource2(config2) {
54207
54244
  method: "POST",
54208
54245
  body: JSON.stringify(input)
54209
54246
  });
54247
+ if (status === 403)
54248
+ throw new Error("Access denied. Only Team Managers can link repos.");
54210
54249
  if (status !== 201)
54211
54250
  throw new Error(`Failed to add repo (${status})`);
54212
54251
  },
@@ -54215,6 +54254,8 @@ function createProjectsResource2(config2) {
54215
54254
  method: "DELETE",
54216
54255
  body: JSON.stringify({ repository })
54217
54256
  });
54257
+ if (status === 403)
54258
+ throw new Error("Access denied. Only Team Managers can link repos.");
54218
54259
  if (status !== 200)
54219
54260
  throw new Error(`Failed to remove repo (${status})`);
54220
54261
  },
@@ -55130,6 +55171,39 @@ function createEndpointChecksResource2(config2) {
55130
55171
  if (status >= 400) {
55131
55172
  throw new Error(data.error ?? `Failed to delete endpoint check (${status})`);
55132
55173
  }
55174
+ },
55175
+ /** Weekly Slack reminder settings (#651). Manager-gated. */
55176
+ async getReminderSettings() {
55177
+ const { data, status } = await apiFetch2(config2, "/endpoint-reminders/settings");
55178
+ if (status === 403) {
55179
+ throw new Error("Access denied. Only Team Managers can view endpoint reminder settings.");
55180
+ }
55181
+ if (status !== 200) {
55182
+ throw new Error(data.error ?? `Failed to fetch endpoint reminder settings (${status})`);
55183
+ }
55184
+ return data.settings;
55185
+ },
55186
+ /** Save the reminder settings (#651). Turning `enabled` on starts the weekly DMs. */
55187
+ async updateReminderSettings(input) {
55188
+ const { data, status } = await apiFetch2(config2, "/endpoint-reminders/settings", { method: "PUT", body: JSON.stringify(input) });
55189
+ if (status === 403) {
55190
+ throw new Error("Access denied. Only Team Managers can change endpoint reminder settings.");
55191
+ }
55192
+ if (status !== 200) {
55193
+ throw new Error(data.error ?? `Failed to save endpoint reminder settings (${status})`);
55194
+ }
55195
+ return data.settings;
55196
+ },
55197
+ /** Who would get a DM this week and who would be in the digest. Sends nothing. */
55198
+ async previewReminders() {
55199
+ const { data, status } = await apiFetch2(config2, "/endpoint-reminders/preview");
55200
+ if (status === 403) {
55201
+ throw new Error("Access denied. Only Team Managers can preview endpoint reminders.");
55202
+ }
55203
+ if (status !== 200) {
55204
+ throw new Error(data.error ?? `Failed to preview endpoint reminders (${status})`);
55205
+ }
55206
+ return data;
55133
55207
  }
55134
55208
  };
55135
55209
  }
@@ -55857,6 +55931,57 @@ async function updateProjectSettings(client, input) {
55857
55931
  async function getProjectContext(client, input) {
55858
55932
  return safe(() => client.pm.getProjectContext(input.projectId));
55859
55933
  }
55934
+ function normalizeRepository(repository) {
55935
+ return repository.trim().replace(/^(https?:\/\/)?(www\.)?github\.com\//i, "").replace(/\.git$/i, "").replace(/\/+$/, "");
55936
+ }
55937
+ var REPOSITORY_PATTERN = /^[\w.-]+\/[\w.-]+$/;
55938
+ async function findProject(client, projectId) {
55939
+ const projects = await client.projects.list({ includeInternal: true });
55940
+ const project2 = projects.find((p) => p.id === projectId);
55941
+ if (!project2) throw new Error(`Project ${projectId} not found. Use list_projects to find its id.`);
55942
+ return { project: project2, projects };
55943
+ }
55944
+ async function linkRepoToProject(client, input) {
55945
+ return safe(async () => {
55946
+ const repository = normalizeRepository(input.repository);
55947
+ if (!REPOSITORY_PATTERN.test(repository)) {
55948
+ throw new Error(`"${input.repository}" isn't a repository in owner/name form.`);
55949
+ }
55950
+ const { project: project2, projects } = await findProject(client, input.projectId);
55951
+ const alreadyLinked = project2.repos.includes(repository);
55952
+ const alsoLinkedTo = projects.filter((p) => p.id !== project2.id && p.repos.includes(repository)).map((p) => ({ projectId: p.id, projectName: p.name }));
55953
+ if (!alreadyLinked) {
55954
+ await client.projects.addRepo(project2.id, { repository, projectName: project2.name });
55955
+ }
55956
+ return {
55957
+ projectId: project2.id,
55958
+ projectName: project2.name,
55959
+ repository,
55960
+ alreadyLinked,
55961
+ alsoLinkedTo,
55962
+ repos: alreadyLinked ? project2.repos : [...project2.repos, repository]
55963
+ };
55964
+ });
55965
+ }
55966
+ async function unlinkRepoFromProject(client, input) {
55967
+ return safe(async () => {
55968
+ const repository = normalizeRepository(input.repository);
55969
+ const { project: project2 } = await findProject(client, input.projectId);
55970
+ const linked = [...project2.repos, ...project2.inactiveRepos];
55971
+ if (!linked.includes(repository)) {
55972
+ throw new Error(
55973
+ `${repository} isn't linked to ${project2.name}. Its repos: ${linked.join(", ") || "none"}.`
55974
+ );
55975
+ }
55976
+ await client.projects.removeRepo(project2.id, repository);
55977
+ return {
55978
+ projectId: project2.id,
55979
+ projectName: project2.name,
55980
+ repository,
55981
+ repos: project2.repos.filter((r) => r !== repository)
55982
+ };
55983
+ });
55984
+ }
55860
55985
  var PROJECT_STAGE_VALUES = ["prototype", "early_revenue", "growth", "scale"];
55861
55986
  var LIFECYCLE_STATUS_VALUES = [
55862
55987
  "commercial",
@@ -55952,6 +56077,30 @@ function registerProjectTools(server, getClient2) {
55952
56077
  },
55953
56078
  withClientArgs(getClient2, getProjectContext)
55954
56079
  );
56080
+ server.registerTool(
56081
+ "link_repo_to_project",
56082
+ {
56083
+ title: "Link a repo to a project",
56084
+ description: "Links a GitHub repository to a Melt project, so its audits, plans and CLI activity count toward that project (health, audit list, dashboard). Accepts `owner/name` or a GitHub URL. Linking a repo that's already linked is a no-op. The response lists the project's repos and `alsoLinkedTo`, any other project that already has this repo; mention those to the user, since a repo usually belongs to one project. Manager-only (403 for others). This is a write: confirm the project and repo with the user first. list_projects shows each project's current repos.",
56085
+ inputSchema: {
56086
+ projectId: external_exports.number().int().positive().describe("Strapi project id from list_projects."),
56087
+ repository: external_exports.string().min(1).describe('GitHub repository as owner/name, e.g. "kateharline/content-engine".')
56088
+ }
56089
+ },
56090
+ withClientArgs(getClient2, linkRepoToProject)
56091
+ );
56092
+ server.registerTool(
56093
+ "unlink_repo_from_project",
56094
+ {
56095
+ title: "Unlink a repo from a project",
56096
+ description: "Removes a repository from a Melt project. Its past audits, plans and events stay in the database but stop counting toward the project. Fails with the project's linked repos if the repo isn't linked. Manager-only (403 for others). This is a write: confirm with the user first.",
56097
+ inputSchema: {
56098
+ projectId: external_exports.number().int().positive().describe("Strapi project id from list_projects."),
56099
+ repository: external_exports.string().min(1).describe("GitHub repository as owner/name.")
56100
+ }
56101
+ },
56102
+ withClientArgs(getClient2, unlinkRepoFromProject)
56103
+ );
55955
56104
  }
55956
56105
  var SHAPE_VALUES = ["crawling", "walking", "running"];
55957
56106
  var SHAPE_DESC = "crawling | walking | running. Target shape is intent (PM sets when creating); current shape is computed from feature stages.";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meltstudio/meltctl",
3
- "version": "5.22.0",
3
+ "version": "5.24.0",
4
4
  "description": "AI-first development tooling for Melt teams — companion CLI for the org-distributed Claude Code dev skills",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",