@meltstudio/meltctl 5.15.0 → 5.17.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 +29 -21
  2. 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.15.0";
7453
+ CLI_VERSION = "5.17.0";
7454
7454
  }
7455
7455
  });
7456
7456
 
@@ -26583,7 +26583,7 @@ function createPlansResource(config2) {
26583
26583
  const path10 = `/plans${query ? `?${query}` : ""}`;
26584
26584
  const { data, status } = await apiFetch(config2, path10);
26585
26585
  if (status === 403)
26586
- throw new Error("Access denied. Only Team Managers can list plans.");
26586
+ throw new Error(`Access denied. ${data.error ?? ""}`.trim());
26587
26587
  if (status !== 200)
26588
26588
  throw new Error(data.error ?? `Failed to list plans (${status})`);
26589
26589
  return data;
@@ -26591,7 +26591,7 @@ function createPlansResource(config2) {
26591
26591
  async get(id) {
26592
26592
  const { data, status } = await apiFetch(config2, `/plans/${id}`);
26593
26593
  if (status === 403)
26594
- throw new Error("Access denied. Only Team Managers can view plans.");
26594
+ throw new Error(`Access denied. ${data.error ?? ""}`.trim());
26595
26595
  if (status === 404)
26596
26596
  throw new Error(`Plan not found: ${id}`);
26597
26597
  if (status !== 200)
@@ -27211,8 +27211,9 @@ function createBoardAuditsResource(config2) {
27211
27211
  // ../sdk/dist/resources/pm.js
27212
27212
  function unwrap(label, response, okStatus = 200) {
27213
27213
  const okStatuses = Array.isArray(okStatus) ? okStatus : [okStatus];
27214
- if (response.status === 403)
27215
- throw new Error(`Access denied (${label})`);
27214
+ if (response.status === 403) {
27215
+ throw new Error(response.data.error ? `Access denied (${label}): ${response.data.error}` : `Access denied (${label})`);
27216
+ }
27216
27217
  if (!okStatuses.includes(response.status)) {
27217
27218
  throw new Error(response.data.error ?? `Failed: ${label} (${response.status})`);
27218
27219
  }
@@ -42621,6 +42622,10 @@ async function getClient() {
42621
42622
  // src/commands/skills.ts
42622
42623
  var MARKER_FILE = ".melt-skills.json";
42623
42624
  function getSkillsRoot() {
42625
+ const configDir = process.env["CLAUDE_CONFIG_DIR"];
42626
+ if (configDir) {
42627
+ return path5.join(configDir, "skills");
42628
+ }
42624
42629
  return path5.join(os5.homedir(), ".claude", "skills");
42625
42630
  }
42626
42631
  function getSkillsRoots() {
@@ -42732,7 +42737,7 @@ async function skillsInstallCommand(opts) {
42732
42737
  );
42733
42738
  process.exit(1);
42734
42739
  }
42735
- const roots = getSkillsRoots();
42740
+ const roots = opts.path ? [path5.resolve(opts.path)] : getSkillsRoots();
42736
42741
  const stale = opts.force ? roots : roots.filter((root) => readMarker(root)?.version !== manifest.version);
42737
42742
  if (stale.length === 0) {
42738
42743
  console.log(
@@ -53253,7 +53258,7 @@ function createPlansResource2(config2) {
53253
53258
  const path22 = `/plans${query ? `?${query}` : ""}`;
53254
53259
  const { data, status } = await apiFetch2(config2, path22);
53255
53260
  if (status === 403)
53256
- throw new Error("Access denied. Only Team Managers can list plans.");
53261
+ throw new Error(`Access denied. ${data.error ?? ""}`.trim());
53257
53262
  if (status !== 200)
53258
53263
  throw new Error(data.error ?? `Failed to list plans (${status})`);
53259
53264
  return data;
@@ -53261,7 +53266,7 @@ function createPlansResource2(config2) {
53261
53266
  async get(id) {
53262
53267
  const { data, status } = await apiFetch2(config2, `/plans/${id}`);
53263
53268
  if (status === 403)
53264
- throw new Error("Access denied. Only Team Managers can view plans.");
53269
+ throw new Error(`Access denied. ${data.error ?? ""}`.trim());
53265
53270
  if (status === 404)
53266
53271
  throw new Error(`Plan not found: ${id}`);
53267
53272
  if (status !== 200)
@@ -53855,8 +53860,9 @@ function createBoardAuditsResource2(config2) {
53855
53860
  }
53856
53861
  function unwrap2(label, response, okStatus = 200) {
53857
53862
  const okStatuses = Array.isArray(okStatus) ? okStatus : [okStatus];
53858
- if (response.status === 403)
53859
- throw new Error(`Access denied (${label})`);
53863
+ if (response.status === 403) {
53864
+ throw new Error(response.data.error ? `Access denied (${label}): ${response.data.error}` : `Access denied (${label})`);
53865
+ }
53860
53866
  if (!okStatuses.includes(response.status)) {
53861
53867
  throw new Error(response.data.error ?? `Failed: ${label} (${response.status})`);
53862
53868
  }
@@ -55246,7 +55252,7 @@ function registerProjectTools(server, getClient2) {
55246
55252
  "list_projects",
55247
55253
  {
55248
55254
  title: "List projects",
55249
- description: "Lists Melt projects the authenticated user can see. Defaults: hasPM=true (only actively-managed projects), includeInternal=false (Melt's own internal projects hidden). 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.",
55255
+ description: "Lists Melt projects the authenticated user can see. Managers see every project; non-managers (devs) see only the projects they're assigned to, PM, or recently worked on \u2014 a project missing from this list is out of their scope, not nonexistent. Defaults: hasPM=true (only actively-managed projects), includeInternal=false (Melt's own internal projects hidden). 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.",
55250
55256
  inputSchema: {
55251
55257
  hasPM: external_exports.boolean().optional().describe(
55252
55258
  "Default true. When true, only projects with a project manager assigned are returned (recommended for AI agent flows). Set false to include unmanaged projects."
@@ -55262,7 +55268,7 @@ function registerProjectTools(server, getClient2) {
55262
55268
  "get_project_settings",
55263
55269
  {
55264
55270
  title: "Get project settings",
55265
- description: 'Returns the project\'s PM-skills settings: cadence (weekly/biweekly/monthly), start_day, demo_day, team_size, velocity_override, Linear workspace, Notion URLs, code repo path, tech-support contact, default language, project glossary, project stage, default jump duration weeks. Includes a `warnings` array flagging settings whose absence breaks downstream behavior (e.g., "calculator: missing teamSize"). Returns sensible defaults if no settings row exists yet (cadence=weekly, start_day=Monday, demo_day=Friday, defaultLanguage=English, defaultJumpDurationWeeks=1).',
55271
+ description: 'Returns the project\'s PM-skills settings: cadence (weekly/biweekly/monthly), start_day, demo_day, team_size, velocity_override, Linear workspace, Notion URLs, code repo path, tech-support contact, default language, project glossary, project stage, default jump duration weeks. Includes a `warnings` array flagging settings whose absence breaks downstream behavior (e.g., "calculator: missing teamSize"). Returns sensible defaults if no settings row exists yet (cadence=weekly, start_day=Monday, demo_day=Friday, defaultLanguage=English, defaultJumpDurationWeeks=1). Non-managers get a 403 for projects outside their assigned/recently-worked scope \u2014 explain the scoping to the user instead of retrying.',
55266
55272
  inputSchema: {
55267
55273
  projectId: external_exports.number().int().positive().describe("Strapi project id from list_projects.")
55268
55274
  }
@@ -55318,7 +55324,7 @@ function registerProjectTools(server, getClient2) {
55318
55324
  "get_project_context",
55319
55325
  {
55320
55326
  title: "Get project context",
55321
- description: "Read-only project identity + the PM-owned canonical project stage. Returns: projectId, projectName, client, pmEmail, repos[], projectStage (prototype | early_revenue | growth | scale | null), projectStageSetBy, projectStageSetAt. Call this at the start of an audit run instead of trusting AGENTS.md self-declaration \u2014 the stage drives audit calibration. Auth-only (no manager check), so devs running the audit skill from their own machine can call it directly.",
55327
+ description: "Read-only project identity + the PM-owned canonical project stage. Returns: projectId, projectName, client, pmEmail, repos[], projectStage (prototype | early_revenue | growth | scale | null), projectStageSetBy, projectStageSetAt. Call this at the start of an audit run instead of trusting AGENTS.md self-declaration \u2014 the stage drives audit calibration. Auth-only (no manager check), so devs running the audit skill from their own machine can call it directly for their own projects; a 403 means the project is outside the dev's assigned/recently-worked scope \u2014 explain the scoping instead of retrying.",
55322
55328
  inputSchema: {
55323
55329
  projectId: external_exports.number().int().positive().describe("Strapi project id from list_projects.")
55324
55330
  }
@@ -55372,7 +55378,7 @@ function registerPhaseTools(server, getClient2) {
55372
55378
  "list_phases",
55373
55379
  {
55374
55380
  title: "List phases",
55375
- description: "Lists phases on a project. Each entry includes target_shape, computed current_shape, primary flag, active flag, the assigned features (with their stage_snapshot at join time + live current_stage), and timestamps. Includes closed phases when includeClosed=true.",
55381
+ description: "Lists phases on a project. Each entry includes target_shape, computed current_shape, primary flag, active flag, the assigned features (with their stage_snapshot at join time + live current_stage), and timestamps. Includes closed phases when includeClosed=true. Non-managers get a 403 for projects outside their assigned/recently-worked scope \u2014 explain the scoping to the user instead of retrying.",
55376
55382
  inputSchema: {
55377
55383
  projectId: external_exports.number().int().positive(),
55378
55384
  includeClosed: external_exports.boolean().optional().describe("Default false. Set true to include closed phases as history.")
@@ -55574,7 +55580,7 @@ function registerFeatureTools(server, getClient2) {
55574
55580
  "list_features",
55575
55581
  {
55576
55582
  title: "List features",
55577
- description: "Lists features on the project. Each entry has id, name, category, description, current_stage, status, prd_path, client_notes, and starting_point. Per-phase target stage + Linear epic live on the phase membership (see list_phases), not the feature row. Optional filters narrow server-side: phaseId returns only features assigned to that phase; status / category / currentStage filter on the matching column.",
55583
+ description: "Lists features on the project. Each entry has id, name, category, description, current_stage, status, prd_path, client_notes, and starting_point. Per-phase target stage + Linear epic live on the phase membership (see list_phases), not the feature row. Optional filters narrow server-side: phaseId returns only features assigned to that phase; status / category / currentStage filter on the matching column. Non-managers get a 403 for projects outside their assigned/recently-worked scope \u2014 explain the scoping to the user instead of retrying.",
55578
55584
  inputSchema: {
55579
55585
  projectId: external_exports.number().int().positive(),
55580
55586
  phaseId: external_exports.string().uuid().optional().describe("Limit to features assigned to this phase (phase_features join)."),
@@ -55690,7 +55696,7 @@ function registerPlanTools(server, getClient2) {
55690
55696
  "list_plans",
55691
55697
  {
55692
55698
  title: "List plans",
55693
- description: "Lists recent dev-submitted plans (list view \u2014 no plan content). Each entry has id, project, repository, author email, branch, commit, ticket id, status, and the aggregate rubric score if scored. Filterable by projectId, status, author (email), ticket id, repository. Defaults to no filters; pass `limit` to cap the return size. Use `get_plan` to read a plan's full markdown content.",
55699
+ description: "Lists recent dev-submitted plans (list view \u2014 no plan content). Each entry has id, project, repository, author email, branch, commit, ticket id, status, and the aggregate rubric score if scored. Filterable by projectId, status, author (email), ticket id, repository. Defaults to no filters; pass `limit` to cap the return size. Use `get_plan` to read a plan's full markdown content. Managers see every plan; non-managers see plans they authored plus plans on repos of their assigned/recently-worked projects.",
55694
55700
  inputSchema: {
55695
55701
  projectId: external_exports.number().int().positive().optional().describe("Strapi project id from list_projects. Omit to list across all projects."),
55696
55702
  status: external_exports.string().optional().describe("Common values: 'submitted', 'in_review', 'completed'."),
@@ -55708,7 +55714,7 @@ function registerPlanTools(server, getClient2) {
55708
55714
  "get_plan",
55709
55715
  {
55710
55716
  title: "Get plan",
55711
- description: "Returns one plan's full detail including the markdown `content`, `scoreSummary` (per-rubric-dimension scoring output), and any metadata the dev submitted. Use this after `list_plans` when the agent needs to read what the plan actually says.",
55717
+ description: "Returns one plan's full detail including the markdown `content`, `scoreSummary` (per-rubric-dimension scoring output), and any metadata the dev submitted. Use this after `list_plans` when the agent needs to read what the plan actually says. Non-managers can read their own plans plus plans on repos of their assigned/recently-worked projects; anything else returns a 403 \u2014 explain the scoping to the user instead of retrying.",
55712
55718
  inputSchema: {
55713
55719
  planId: external_exports.string().uuid()
55714
55720
  }
@@ -55783,7 +55789,7 @@ function registerProjectHealthTools(server, getClient2) {
55783
55789
  "get_project_health",
55784
55790
  {
55785
55791
  title: "Get project health",
55786
- description: "Composite read-only snapshot of one project's health: 0-100 score; `issues` \u2014 the full list of open health issues (each with label, dimension, severity, and a one-line howToFix), which are the EXACT items shown on the project's dashboard health card (e.g. 'PM silent in active cycle', 'Roadmap status differs from Linear', 'No roadmap'); audit-freshness numbers, findings (critical/high/missing/warning counts), board-audit aggregate + age, ticket counts (open/started/overdue), standup compliance (last 14 days), plan recency, last CLI activity, and the project's RSK risks (open/mitigated counts, red-zone count, top 3 open risks with impact \xD7 probability scores AND the PM's latest weekly status comment on each one). Risks + comments come from a weekly mirror of the RSK Linear board, refreshed every Tuesday evening right after the team's risk-review meeting \u2014 so 'latestComment' is normally the just-logged update for the week. Use this for 'how's project X doing?' / 'what needs attention on X?' questions \u2014 prefer surfacing the `issues` list (worst severity first) over just the score. Pulls in one round trip what would otherwise be 8+ separate tool calls; score + issues match the dashboard's project cards.",
55792
+ description: "Composite read-only snapshot of one project's health: 0-100 score; `issues` \u2014 the full list of open health issues (each with label, dimension, severity, and a one-line howToFix), which are the EXACT items shown on the project's dashboard health card (e.g. 'PM silent in active cycle', 'Roadmap status differs from Linear', 'No roadmap'); audit-freshness numbers, findings (critical/high/missing/warning counts), board-audit aggregate + age, ticket counts (open/started/overdue), standup compliance (last 14 days), plan recency, last CLI activity, and the project's RSK risks (open/mitigated counts, red-zone count, top 3 open risks with impact \xD7 probability scores AND the PM's latest weekly status comment on each one). Risks + comments come from a weekly mirror of the RSK Linear board, refreshed every Tuesday evening right after the team's risk-review meeting \u2014 so 'latestComment' is normally the just-logged update for the week. Use this for 'how's project X doing?' / 'what needs attention on X?' questions \u2014 prefer surfacing the `issues` list (worst severity first) over just the score. Pulls in one round trip what would otherwise be 8+ separate tool calls; score + issues match the dashboard's project cards. Non-managers get a 403 for projects outside their assigned/recently-worked scope \u2014 explain the scoping to the user instead of retrying.",
55787
55793
  inputSchema: {
55788
55794
  projectId: external_exports.number().int().positive().describe(
55789
55795
  "Strapi project id from list_projects. The project must have at least one linked repository \u2014 otherwise the score is undefined and the tool returns 422."
@@ -55836,7 +55842,7 @@ function registerFindingsTools(server, getClient2) {
55836
55842
  "list_findings",
55837
55843
  {
55838
55844
  title: "List audit findings",
55839
- description: "Lists code/security/UX audit findings. Each finding carries its check code, category, status (pass/warning/missing/na), severity (critical/high/medium/low), effort (low/medium/high \u2014 how much work the fix is, independent of severity), repository, and evidence (file/symbol) so you can see exactly what to fix. Filter by projectId, repository, status, severity, effort, or auditType. Results are ordered worst-first (missing > warning, then critical > high > \u2026). Use this to answer 'what's open on the app I'm working on?' \u2014 pass status='missing' or severity='critical' to focus on what matters, or effort='low' with severity='high'/'critical' to find cheap high-leverage wins. Read-only and open to any @meltstudio.co user.",
55845
+ description: "Lists code/security/UX audit findings. Each finding carries its check code, category, status (pass/warning/missing/na), severity (critical/high/medium/low), effort (low/medium/high \u2014 how much work the fix is, independent of severity), repository, and evidence (file/symbol) so you can see exactly what to fix. Filter by projectId, repository, status, severity, effort, or auditType. Results are ordered worst-first (missing > warning, then critical > high > \u2026). Use this to answer 'what's open on the app I'm working on?' \u2014 pass status='missing' or severity='critical' to focus on what matters, or effort='low' with severity='high'/'critical' to find cheap high-leverage wins. Read-only; managers see every finding, while non-managers see only findings on repos of their assigned/recently-worked projects (an empty result may mean out-of-scope, not clean).",
55840
55846
  inputSchema: {
55841
55847
  projectId: external_exports.number().int().positive().optional().describe("Strapi project id \u2014 scopes findings to that project\u2019s repos."),
55842
55848
  repository: external_exports.string().optional().describe("Full repo slug, e.g. 'MeltStudio/atlas-api'. Scopes to a single repo."),
@@ -56050,7 +56056,7 @@ ${source_default.dim(" /melt-dev-security-audit Run a security posture audit
56050
56056
  ${source_default.dim(" /melt-dev-help Answer questions about the development playbook")}
56051
56057
 
56052
56058
  ${source_default.bold("Setup")}
56053
- ${source_default.dim(" skills install Install the dev skills (/melt-dev-*) into Claude Code (+ Cursor)")}
56059
+ ${source_default.dim(" skills install Install the dev skills (/melt-dev-*) into Claude Code (+ Cursor; respects CLAUDE_CONFIG_DIR, or --path <dir>)")}
56054
56060
 
56055
56061
  ${source_default.bold("Admin Commands")} ${source_default.dim("(used by skills and team managers):")}
56056
56062
  ${source_default.dim(" audit submit [file] Submit an audit report")}
@@ -56091,7 +56097,9 @@ program2.command("mcp").description(
56091
56097
  await mcpCommand();
56092
56098
  });
56093
56099
  var skills = program2.command("skills").description("install the Melt dev skills (/melt-dev-*) into Claude Code");
56094
- skills.command("install").description("install the Melt dev skills into ~/.claude/skills/ (re-run to update)").option("--force", "reinstall even if already up to date").action(async (options) => {
56100
+ skills.command("install").description(
56101
+ "install the Melt dev skills into ~/.claude/skills/ (re-run to update; respects CLAUDE_CONFIG_DIR)"
56102
+ ).option("--force", "reinstall even if already up to date").option("--path <dir>", "install into this directory only (overrides CLAUDE_CONFIG_DIR)").action(async (options) => {
56095
56103
  await skillsInstallCommand(options);
56096
56104
  });
56097
56105
  var project = program2.command("project").description("manage project configuration").addHelpText(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meltstudio/meltctl",
3
- "version": "5.15.0",
3
+ "version": "5.17.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",