@meltstudio/meltctl 5.22.0 → 5.23.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 +84 -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.23.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
  },
@@ -54207,6 +54211,8 @@ function createProjectsResource2(config2) {
54207
54211
  method: "POST",
54208
54212
  body: JSON.stringify(input)
54209
54213
  });
54214
+ if (status === 403)
54215
+ throw new Error("Access denied. Only Team Managers can link repos.");
54210
54216
  if (status !== 201)
54211
54217
  throw new Error(`Failed to add repo (${status})`);
54212
54218
  },
@@ -54215,6 +54221,8 @@ function createProjectsResource2(config2) {
54215
54221
  method: "DELETE",
54216
54222
  body: JSON.stringify({ repository })
54217
54223
  });
54224
+ if (status === 403)
54225
+ throw new Error("Access denied. Only Team Managers can link repos.");
54218
54226
  if (status !== 200)
54219
54227
  throw new Error(`Failed to remove repo (${status})`);
54220
54228
  },
@@ -55857,6 +55865,57 @@ async function updateProjectSettings(client, input) {
55857
55865
  async function getProjectContext(client, input) {
55858
55866
  return safe(() => client.pm.getProjectContext(input.projectId));
55859
55867
  }
55868
+ function normalizeRepository(repository) {
55869
+ return repository.trim().replace(/^(https?:\/\/)?(www\.)?github\.com\//i, "").replace(/\.git$/i, "").replace(/\/+$/, "");
55870
+ }
55871
+ var REPOSITORY_PATTERN = /^[\w.-]+\/[\w.-]+$/;
55872
+ async function findProject(client, projectId) {
55873
+ const projects = await client.projects.list({ includeInternal: true });
55874
+ const project2 = projects.find((p) => p.id === projectId);
55875
+ if (!project2) throw new Error(`Project ${projectId} not found. Use list_projects to find its id.`);
55876
+ return { project: project2, projects };
55877
+ }
55878
+ async function linkRepoToProject(client, input) {
55879
+ return safe(async () => {
55880
+ const repository = normalizeRepository(input.repository);
55881
+ if (!REPOSITORY_PATTERN.test(repository)) {
55882
+ throw new Error(`"${input.repository}" isn't a repository in owner/name form.`);
55883
+ }
55884
+ const { project: project2, projects } = await findProject(client, input.projectId);
55885
+ const alreadyLinked = project2.repos.includes(repository);
55886
+ const alsoLinkedTo = projects.filter((p) => p.id !== project2.id && p.repos.includes(repository)).map((p) => ({ projectId: p.id, projectName: p.name }));
55887
+ if (!alreadyLinked) {
55888
+ await client.projects.addRepo(project2.id, { repository, projectName: project2.name });
55889
+ }
55890
+ return {
55891
+ projectId: project2.id,
55892
+ projectName: project2.name,
55893
+ repository,
55894
+ alreadyLinked,
55895
+ alsoLinkedTo,
55896
+ repos: alreadyLinked ? project2.repos : [...project2.repos, repository]
55897
+ };
55898
+ });
55899
+ }
55900
+ async function unlinkRepoFromProject(client, input) {
55901
+ return safe(async () => {
55902
+ const repository = normalizeRepository(input.repository);
55903
+ const { project: project2 } = await findProject(client, input.projectId);
55904
+ const linked = [...project2.repos, ...project2.inactiveRepos];
55905
+ if (!linked.includes(repository)) {
55906
+ throw new Error(
55907
+ `${repository} isn't linked to ${project2.name}. Its repos: ${linked.join(", ") || "none"}.`
55908
+ );
55909
+ }
55910
+ await client.projects.removeRepo(project2.id, repository);
55911
+ return {
55912
+ projectId: project2.id,
55913
+ projectName: project2.name,
55914
+ repository,
55915
+ repos: project2.repos.filter((r) => r !== repository)
55916
+ };
55917
+ });
55918
+ }
55860
55919
  var PROJECT_STAGE_VALUES = ["prototype", "early_revenue", "growth", "scale"];
55861
55920
  var LIFECYCLE_STATUS_VALUES = [
55862
55921
  "commercial",
@@ -55952,6 +56011,30 @@ function registerProjectTools(server, getClient2) {
55952
56011
  },
55953
56012
  withClientArgs(getClient2, getProjectContext)
55954
56013
  );
56014
+ server.registerTool(
56015
+ "link_repo_to_project",
56016
+ {
56017
+ title: "Link a repo to a project",
56018
+ 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.",
56019
+ inputSchema: {
56020
+ projectId: external_exports.number().int().positive().describe("Strapi project id from list_projects."),
56021
+ repository: external_exports.string().min(1).describe('GitHub repository as owner/name, e.g. "kateharline/content-engine".')
56022
+ }
56023
+ },
56024
+ withClientArgs(getClient2, linkRepoToProject)
56025
+ );
56026
+ server.registerTool(
56027
+ "unlink_repo_from_project",
56028
+ {
56029
+ title: "Unlink a repo from a project",
56030
+ 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.",
56031
+ inputSchema: {
56032
+ projectId: external_exports.number().int().positive().describe("Strapi project id from list_projects."),
56033
+ repository: external_exports.string().min(1).describe("GitHub repository as owner/name.")
56034
+ }
56035
+ },
56036
+ withClientArgs(getClient2, unlinkRepoFromProject)
56037
+ );
55955
56038
  }
55956
56039
  var SHAPE_VALUES = ["crawling", "walking", "running"];
55957
56040
  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.23.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",