@meltstudio/meltctl 4.117.1 → 4.118.1

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 +89 -61
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var CLI_VERSION;
14
14
  var init_version = __esm({
15
15
  "src/utils/version.ts"() {
16
16
  "use strict";
17
- CLI_VERSION = "4.117.1";
17
+ CLI_VERSION = "4.118.1";
18
18
  }
19
19
  });
20
20
 
@@ -164,10 +164,16 @@ function getUpdateSeverity(current, latest) {
164
164
  if (latMajor === curMajor && latMinor === curMinor && latPatch > curPatch) return "patch";
165
165
  return "none";
166
166
  }
167
- async function checkAndEnforceUpdate() {
167
+ function isAlwaysRunnable(commandName) {
168
+ return commandName !== void 0 && ALWAYS_RUNNABLE_COMMANDS.has(commandName);
169
+ }
170
+ async function checkAndEnforceUpdate(commandName) {
168
171
  if (isCI()) {
169
172
  return;
170
173
  }
174
+ if (isAlwaysRunnable(commandName)) {
175
+ return;
176
+ }
171
177
  try {
172
178
  const currentVersion = await getCurrentCliVersion();
173
179
  const latestVersion = await getLatestCliVersion();
@@ -211,10 +217,12 @@ async function checkAndEnforceUpdate() {
211
217
  return;
212
218
  }
213
219
  }
220
+ var ALWAYS_RUNNABLE_COMMANDS;
214
221
  var init_version_check = __esm({
215
222
  "src/utils/version-check.ts"() {
216
223
  "use strict";
217
224
  init_version();
225
+ ALWAYS_RUNNABLE_COMMANDS = /* @__PURE__ */ new Set(["login", "logout", "update", "version", "mcp", "help"]);
218
226
  }
219
227
  });
220
228
 
@@ -1893,13 +1901,13 @@ async function loginCommand() {
1893
1901
  server.listen(port, () => {
1894
1902
  const authUrl = `${API_BASE}/auth/google?redirect_uri=${encodeURIComponent(redirectUri)}`;
1895
1903
  console.log();
1896
- console.log(chalk5.bold(" A browser window will open shortly."));
1897
- console.log(chalk5.dim(" Please log in with your @meltstudio.co Google Workspace account."));
1904
+ console.log(chalk5.bold(" Open this URL to log in (or wait for your browser to open):"));
1898
1905
  console.log();
1899
- setTimeout(() => {
1900
- openBrowser(authUrl);
1901
- console.log(chalk5.dim(` If the browser didn't open, visit: ${authUrl}`));
1902
- }, 2e3);
1906
+ console.log(` ${chalk5.cyan(authUrl)}`);
1907
+ console.log();
1908
+ console.log(chalk5.dim(" Sign in with your @meltstudio.co Google Workspace account."));
1909
+ console.log();
1910
+ openBrowser(authUrl);
1903
1911
  });
1904
1912
  });
1905
1913
  console.log(chalk5.dim("Exchanging authorization code..."));
@@ -3647,7 +3655,7 @@ async function loadAuth() {
3647
3655
  } catch (err) {
3648
3656
  if (err.code === "ENOENT") {
3649
3657
  throw new AuthError(
3650
- "No meltctl session found. Run `npx @meltstudio/meltctl@latest login` first, then restart your MCP-enabled editor."
3658
+ "meltctl tools are disabled until you log in. Run `npx @meltstudio/meltctl@latest login`, then restart this editor so the MCP server picks up the new token."
3651
3659
  );
3652
3660
  }
3653
3661
  throw err;
@@ -3662,7 +3670,7 @@ async function loadAuth() {
3662
3670
  }
3663
3671
  if (new Date(stored.expiresAt) <= /* @__PURE__ */ new Date()) {
3664
3672
  throw new AuthError(
3665
- "meltctl session expired. Run `npx @meltstudio/meltctl@latest login` to refresh, then restart your MCP-enabled editor."
3673
+ "meltctl tools are disabled because the saved session has expired. Run `npx @meltstudio/meltctl@latest login` to refresh it, then restart this editor."
3666
3674
  );
3667
3675
  }
3668
3676
  return stored;
@@ -3691,13 +3699,35 @@ async function safe(fn) {
3691
3699
  return fail(message);
3692
3700
  }
3693
3701
  }
3702
+ function withClient(getClient2, handler) {
3703
+ return async () => {
3704
+ try {
3705
+ const client = await getClient2();
3706
+ return await handler(client);
3707
+ } catch (err) {
3708
+ const message = err instanceof Error ? err.message : String(err);
3709
+ return fail(message);
3710
+ }
3711
+ };
3712
+ }
3713
+ function withClientArgs(getClient2, handler) {
3714
+ return async (args) => {
3715
+ try {
3716
+ const client = await getClient2();
3717
+ return await handler(client, args);
3718
+ } catch (err) {
3719
+ const message = err instanceof Error ? err.message : String(err);
3720
+ return fail(message);
3721
+ }
3722
+ };
3723
+ }
3694
3724
  async function listProjects(client) {
3695
3725
  return safe(() => client.projects.list());
3696
3726
  }
3697
3727
  async function getProjectSettings(client, input3) {
3698
3728
  return safe(() => client.pm.getProjectSettings(input3.projectId));
3699
3729
  }
3700
- function registerProjectTools(server, client) {
3730
+ function registerProjectTools(server, getClient2) {
3701
3731
  server.registerTool(
3702
3732
  "list_projects",
3703
3733
  {
@@ -3705,7 +3735,7 @@ function registerProjectTools(server, client) {
3705
3735
  description: "Lists every Melt project the authenticated user can see. Use this to resolve a project's name to its numeric `projectId` before calling any other tool. Returns id, name, client, project manager, and repo count per project.",
3706
3736
  inputSchema: {}
3707
3737
  },
3708
- () => listProjects(client)
3738
+ withClient(getClient2, listProjects)
3709
3739
  );
3710
3740
  server.registerTool(
3711
3741
  "get_project_settings",
@@ -3716,7 +3746,7 @@ function registerProjectTools(server, client) {
3716
3746
  projectId: z22.number().int().positive().describe("Strapi project id from list_projects.")
3717
3747
  }
3718
3748
  },
3719
- (args) => getProjectSettings(client, args)
3749
+ withClientArgs(getClient2, getProjectSettings)
3720
3750
  );
3721
3751
  }
3722
3752
  var SHAPE_DESC = "crawling | walking | running. Target shape is intent (PM sets when creating); current shape is computed from feature stages.";
@@ -3745,7 +3775,7 @@ var createPhaseInputSchema = z3.object({
3745
3775
  targetShape: z3.enum(["crawling", "walking", "running"]).default("walking"),
3746
3776
  isPrimary: z3.boolean().optional()
3747
3777
  });
3748
- function registerPhaseTools(server, client) {
3778
+ function registerPhaseTools(server, getClient2) {
3749
3779
  server.registerTool(
3750
3780
  "list_phases",
3751
3781
  {
@@ -3756,7 +3786,7 @@ function registerPhaseTools(server, client) {
3756
3786
  includeClosed: z3.boolean().optional().describe("Default false. Set true to include closed phases as history.")
3757
3787
  }
3758
3788
  },
3759
- (args) => listPhases(client, args)
3789
+ withClientArgs(getClient2, listPhases)
3760
3790
  );
3761
3791
  server.registerTool(
3762
3792
  "create_phase",
@@ -3772,7 +3802,7 @@ function registerPhaseTools(server, client) {
3772
3802
  )
3773
3803
  }
3774
3804
  },
3775
- (args) => createPhase(client, args)
3805
+ withClientArgs(getClient2, createPhase)
3776
3806
  );
3777
3807
  server.registerTool(
3778
3808
  "assign_feature_to_phase",
@@ -3784,7 +3814,7 @@ function registerPhaseTools(server, client) {
3784
3814
  featureId: z3.string().uuid().describe("Feature id from list_features or create_feature.")
3785
3815
  }
3786
3816
  },
3787
- (args) => assignFeatureToPhase(client, args)
3817
+ withClientArgs(getClient2, assignFeatureToPhase)
3788
3818
  );
3789
3819
  server.registerTool(
3790
3820
  "unassign_feature_from_phase",
@@ -3796,7 +3826,7 @@ function registerPhaseTools(server, client) {
3796
3826
  featureId: z3.string().uuid().describe("Feature id of the membership to remove. The feature itself is not deleted.")
3797
3827
  }
3798
3828
  },
3799
- (args) => unassignFeatureFromPhase(client, args)
3829
+ withClientArgs(getClient2, unassignFeatureFromPhase)
3800
3830
  );
3801
3831
  }
3802
3832
  var STAGE_VALUES = ["idea", "poc", "pt", "mv", "mk", "ma", "mbi"];
@@ -3834,7 +3864,7 @@ var updateFeatureInputSchema = z4.object({
3834
3864
  clientDependencies: z4.string().optional(),
3835
3865
  linearEpicUrl: z4.string().url().nullable().optional()
3836
3866
  });
3837
- function registerFeatureTools(server, client) {
3867
+ function registerFeatureTools(server, getClient2) {
3838
3868
  server.registerTool(
3839
3869
  "list_features",
3840
3870
  {
@@ -3844,7 +3874,7 @@ function registerFeatureTools(server, client) {
3844
3874
  projectId: z4.number().int().positive()
3845
3875
  }
3846
3876
  },
3847
- (args) => listFeatures(client, args)
3877
+ withClientArgs(getClient2, listFeatures)
3848
3878
  );
3849
3879
  server.registerTool(
3850
3880
  "create_feature",
@@ -3863,7 +3893,7 @@ function registerFeatureTools(server, client) {
3863
3893
  clientDependencies: z4.string().optional().describe("Free-text. What blockers from the client side, if any.")
3864
3894
  }
3865
3895
  },
3866
- (args) => createFeature(client, args)
3896
+ withClientArgs(getClient2, createFeature)
3867
3897
  );
3868
3898
  server.registerTool(
3869
3899
  "update_feature",
@@ -3885,7 +3915,7 @@ function registerFeatureTools(server, client) {
3885
3915
  )
3886
3916
  }
3887
3917
  },
3888
- (args) => updateFeature(client, args)
3918
+ withClientArgs(getClient2, updateFeature)
3889
3919
  );
3890
3920
  }
3891
3921
  var FLAG_STATUS_VALUES = ["open", "resolved", "dismissed", "all"];
@@ -3909,7 +3939,7 @@ var dismissAuditFlagInputSchema = z5.object({
3909
3939
  flagId: z5.string().uuid(),
3910
3940
  reason: z5.string().min(1)
3911
3941
  });
3912
- function registerAuditTools(server, client) {
3942
+ function registerAuditTools(server, getClient2) {
3913
3943
  server.registerTool(
3914
3944
  "list_audit_flags",
3915
3945
  {
@@ -3922,7 +3952,7 @@ function registerAuditTools(server, client) {
3922
3952
  )
3923
3953
  }
3924
3954
  },
3925
- (args) => listAuditFlags(client, args)
3955
+ withClientArgs(getClient2, listAuditFlags)
3926
3956
  );
3927
3957
  server.registerTool(
3928
3958
  "get_audit_catalog",
@@ -3931,7 +3961,7 @@ function registerAuditTools(server, client) {
3931
3961
  description: "Returns every audit rule the system knows about (code, category, title, description, severity, rubric version). Use this to explain to a PM what a specific flag means, or to enumerate what kinds of things we audit for. Catalog is read-only \u2014 new entries land via migration.",
3932
3962
  inputSchema: {}
3933
3963
  },
3934
- () => getAuditCatalog(client)
3964
+ withClient(getClient2, getAuditCatalog)
3935
3965
  );
3936
3966
  server.registerTool(
3937
3967
  "dismiss_audit_flag",
@@ -3945,7 +3975,7 @@ function registerAuditTools(server, client) {
3945
3975
  )
3946
3976
  }
3947
3977
  },
3948
- (args) => dismissAuditFlag(client, args)
3978
+ withClientArgs(getClient2, dismissAuditFlag)
3949
3979
  );
3950
3980
  server.registerTool(
3951
3981
  "run_project_audit",
@@ -3956,7 +3986,7 @@ function registerAuditTools(server, client) {
3956
3986
  projectId: z5.number().int().positive()
3957
3987
  }
3958
3988
  },
3959
- (args) => runProjectAudit(client, args)
3989
+ withClientArgs(getClient2, runProjectAudit)
3960
3990
  );
3961
3991
  }
3962
3992
  async function listPlans(client, input3 = {}) {
@@ -3973,7 +4003,7 @@ var listPlansInputSchema = z6.object({
3973
4003
  repository: z6.string().optional(),
3974
4004
  limit: z6.number().int().positive().max(200).optional()
3975
4005
  });
3976
- function registerPlanTools(server, client) {
4006
+ function registerPlanTools(server, getClient2) {
3977
4007
  server.registerTool(
3978
4008
  "list_plans",
3979
4009
  {
@@ -3990,7 +4020,7 @@ function registerPlanTools(server, client) {
3990
4020
  limit: z6.number().int().positive().max(200).optional().describe("Cap the return size. Default is whatever the API returns (typically 50).")
3991
4021
  }
3992
4022
  },
3993
- (args) => listPlans(client, args)
4023
+ withClientArgs(getClient2, listPlans)
3994
4024
  );
3995
4025
  server.registerTool(
3996
4026
  "get_plan",
@@ -4001,7 +4031,7 @@ function registerPlanTools(server, client) {
4001
4031
  planId: z6.string().uuid()
4002
4032
  }
4003
4033
  },
4004
- (args) => getPlan(client, args)
4034
+ withClientArgs(getClient2, getPlan)
4005
4035
  );
4006
4036
  }
4007
4037
  async function listBoardAudits(client, input3 = {}) {
@@ -4020,7 +4050,7 @@ var listBoardAuditsInputSchema = z7.object({
4020
4050
  projectId: z7.number().int().positive().optional(),
4021
4051
  limit: z7.number().int().positive().max(200).optional()
4022
4052
  });
4023
- function registerBoardAuditTools(server, client) {
4053
+ function registerBoardAuditTools(server, getClient2) {
4024
4054
  server.registerTool(
4025
4055
  "list_board_audits",
4026
4056
  {
@@ -4031,7 +4061,7 @@ function registerBoardAuditTools(server, client) {
4031
4061
  limit: z7.number().int().positive().max(200).optional()
4032
4062
  }
4033
4063
  },
4034
- (args) => listBoardAudits(client, args)
4064
+ withClientArgs(getClient2, listBoardAudits)
4035
4065
  );
4036
4066
  server.registerTool(
4037
4067
  "get_board_audit",
@@ -4042,7 +4072,7 @@ function registerBoardAuditTools(server, client) {
4042
4072
  auditId: z7.string().uuid()
4043
4073
  }
4044
4074
  },
4045
- (args) => getBoardAudit(client, args)
4075
+ withClientArgs(getClient2, getBoardAudit)
4046
4076
  );
4047
4077
  server.registerTool(
4048
4078
  "list_latest_board_audits",
@@ -4051,7 +4081,7 @@ function registerBoardAuditTools(server, client) {
4051
4081
  description: "Cross-project rollup: one entry per project showing its most recent board audit (score, severity-binned finding counts, run timestamp). Use this for questions like 'which of my projects has the worst board hygiene right now?' Cheaper than calling `list_board_audits` per project.",
4052
4082
  inputSchema: {}
4053
4083
  },
4054
- () => listLatestBoardAudits(client)
4084
+ withClient(getClient2, listLatestBoardAudits)
4055
4085
  );
4056
4086
  server.registerTool(
4057
4087
  "get_board_audit_catalog",
@@ -4060,13 +4090,13 @@ function registerBoardAuditTools(server, client) {
4060
4090
  description: "Returns every board-audit rule the system knows about (code, category, title, description, severity). Complements `get_audit_catalog` (which is for project-level flags); this one covers ticket-tracker hygiene checks.",
4061
4091
  inputSchema: {}
4062
4092
  },
4063
- () => getBoardAuditCatalog(client)
4093
+ withClient(getClient2, getBoardAuditCatalog)
4064
4094
  );
4065
4095
  }
4066
4096
  async function getProjectHealth(client, input3) {
4067
4097
  return safe(() => client.pm.getProjectHealth(input3.projectId));
4068
4098
  }
4069
- function registerProjectHealthTools(server, client) {
4099
+ function registerProjectHealthTools(server, getClient2) {
4070
4100
  server.registerTool(
4071
4101
  "get_project_health",
4072
4102
  {
@@ -4078,7 +4108,7 @@ function registerProjectHealthTools(server, client) {
4078
4108
  )
4079
4109
  }
4080
4110
  },
4081
- (args) => getProjectHealth(client, args)
4111
+ withClientArgs(getClient2, getProjectHealth)
4082
4112
  );
4083
4113
  }
4084
4114
  async function getMyPortfolioStatus(client, input3) {
@@ -4089,7 +4119,7 @@ async function getMyPortfolioStatus(client, input3) {
4089
4119
  })
4090
4120
  );
4091
4121
  }
4092
- function registerPortfolioStatusTools(server, client) {
4122
+ function registerPortfolioStatusTools(server, getClient2) {
4093
4123
  server.registerTool(
4094
4124
  "get_my_portfolio_status",
4095
4125
  {
@@ -4104,38 +4134,32 @@ function registerPortfolioStatusTools(server, client) {
4104
4134
  )
4105
4135
  }
4106
4136
  },
4107
- (args) => getMyPortfolioStatus(client, args)
4137
+ withClientArgs(getClient2, getMyPortfolioStatus)
4108
4138
  );
4109
4139
  }
4110
4140
  var VERSION = "0.0.0";
4111
- function createMcpServer(client) {
4141
+ function createMcpServer(clientOrProvider) {
4142
+ const getClient2 = typeof clientOrProvider === "function" ? clientOrProvider : () => Promise.resolve(clientOrProvider);
4112
4143
  const server = new McpServer(
4113
4144
  { name: "meltctl-mcp", version: VERSION },
4114
4145
  { capabilities: { tools: {} } }
4115
4146
  );
4116
- registerProjectTools(server, client);
4117
- registerPhaseTools(server, client);
4118
- registerFeatureTools(server, client);
4119
- registerAuditTools(server, client);
4120
- registerPlanTools(server, client);
4121
- registerBoardAuditTools(server, client);
4122
- registerProjectHealthTools(server, client);
4123
- registerPortfolioStatusTools(server, client);
4147
+ registerProjectTools(server, getClient2);
4148
+ registerPhaseTools(server, getClient2);
4149
+ registerFeatureTools(server, getClient2);
4150
+ registerAuditTools(server, getClient2);
4151
+ registerPlanTools(server, getClient2);
4152
+ registerBoardAuditTools(server, getClient2);
4153
+ registerProjectHealthTools(server, getClient2);
4154
+ registerPortfolioStatusTools(server, getClient2);
4124
4155
  return server;
4125
4156
  }
4126
4157
  async function startServer() {
4127
- let client;
4128
- try {
4158
+ const getClient2 = async () => {
4129
4159
  const auth = await loadAuth();
4130
- client = createMeltClient2({ baseUrl: API_BASE2, token: auth.token });
4131
- } catch (err) {
4132
- if (err instanceof AuthError) {
4133
- console.error(`[meltctl-mcp] ${err.message}`);
4134
- process.exit(1);
4135
- }
4136
- throw err;
4137
- }
4138
- const server = createMcpServer(client);
4160
+ return createMeltClient2({ baseUrl: API_BASE2, token: auth.token });
4161
+ };
4162
+ const server = createMcpServer(getClient2);
4139
4163
  const transport = new StdioServerTransport();
4140
4164
  await server.connect(transport);
4141
4165
  }
@@ -4185,8 +4209,12 @@ ${chalk16.dim(" plan submit [file] Submit or update a plan")}
4185
4209
  ${chalk16.dim(" plan list List plans (managers only)")}
4186
4210
  ${chalk16.dim(" event track --skill Track skill usage (called by skills automatically)")}
4187
4211
  `
4188
- ).hook("preAction", async () => {
4189
- await checkAndEnforceUpdate();
4212
+ ).hook("preAction", async (_thisCommand, actionCommand) => {
4213
+ let cmd = actionCommand;
4214
+ while (cmd.parent && cmd.parent.name() !== "meltctl") {
4215
+ cmd = cmd.parent;
4216
+ }
4217
+ await checkAndEnforceUpdate(cmd.name());
4190
4218
  }).hook("postAction", async (_thisCommand, actionCommand) => {
4191
4219
  try {
4192
4220
  const parts = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meltstudio/meltctl",
3
- "version": "4.117.1",
3
+ "version": "4.118.1",
4
4
  "description": "AI-first development tools for teams - set up AGENTS.md, Claude Code, Cursor, and OpenCode standards",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",