@meltstudio/meltctl 4.6.0 → 4.6.2

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.
@@ -79,6 +79,11 @@ async function promptToolSelection(existingTools) {
79
79
  };
80
80
  }
81
81
  export async function initCommand(options) {
82
+ // Check authentication before anything else to avoid wasted prompts
83
+ if (!(await isAuthenticated())) {
84
+ console.error(chalk.red('Not authenticated. Run `npx @meltstudio/meltctl@latest login` first.'));
85
+ process.exit(1);
86
+ }
82
87
  const cwd = process.cwd();
83
88
  const alreadyInitialized = await fs.pathExists(path.join(cwd, 'AGENTS.md'));
84
89
  let isReInit = false;
@@ -123,18 +128,13 @@ export async function initCommand(options) {
123
128
  console.log(chalk.yellow('No tools selected. Nothing to do.'));
124
129
  process.exit(0);
125
130
  }
126
- // Require authentication
127
- if (!(await isAuthenticated())) {
128
- console.error(chalk.red('Not authenticated. Run `meltctl login` first.'));
129
- process.exit(1);
130
- }
131
131
  let templates;
132
132
  try {
133
133
  templates = await fetchTemplates();
134
134
  }
135
135
  catch (error) {
136
136
  if (error instanceof Error && error.message.includes('expired')) {
137
- console.error(chalk.red('Session expired. Run `meltctl login` to re-authenticate.'));
137
+ console.error(chalk.red('Session expired. Run `npx @meltstudio/meltctl@latest login` to re-authenticate.'));
138
138
  }
139
139
  else if (error instanceof Error && error.message.includes('fetch')) {
140
140
  console.error(chalk.red('Could not reach Melt API. Check your connection.'));
@@ -167,7 +167,7 @@ export async function initCommand(options) {
167
167
  const claudeSettings = templates['claude-settings.json'];
168
168
  if (claudeSettings) {
169
169
  await fs.ensureDir(path.join(cwd, '.claude'));
170
- await fs.writeFile(path.join(cwd, '.claude/settings.json'), claudeSettings, 'utf-8');
170
+ await mergeClaudeSettings(cwd, claudeSettings);
171
171
  createdFiles.push('.claude/settings.json');
172
172
  }
173
173
  for (const name of workflows) {
@@ -214,7 +214,8 @@ export async function initCommand(options) {
214
214
  }
215
215
  if (!isReInit) {
216
216
  const setupCmd = tools.claude ? '/melt-setup' : 'melt-setup';
217
- const msg = `Next: run ${setupCmd} to customize AGENTS.md for your project`;
217
+ const where = tools.claude ? 'in Claude Code' : 'in Cursor';
218
+ const msg = `Next: open ${where} and run ${setupCmd} to customize AGENTS.md`;
218
219
  const pad = 3;
219
220
  const inner = msg.length + pad * 2;
220
221
  const line = '─'.repeat(inner);
@@ -246,6 +247,28 @@ async function mergeMcpConfig(cwd, templateContent) {
246
247
  await fs.writeFile(mcpPath, templateContent, 'utf-8');
247
248
  }
248
249
  }
250
+ async function mergeClaudeSettings(cwd, templateContent) {
251
+ const settingsPath = path.join(cwd, '.claude/settings.json');
252
+ const templateConfig = JSON.parse(templateContent);
253
+ if (await fs.pathExists(settingsPath)) {
254
+ const existingContent = await fs.readFile(settingsPath, 'utf-8');
255
+ const existingConfig = JSON.parse(existingContent);
256
+ // Merge permissions: combine allow/deny lists, deduplicate
257
+ const existingAllow = existingConfig.permissions?.allow ?? [];
258
+ const existingDeny = existingConfig.permissions?.deny ?? [];
259
+ const templateAllow = templateConfig.permissions?.allow ?? [];
260
+ const templateDeny = templateConfig.permissions?.deny ?? [];
261
+ existingConfig.permissions = {
262
+ ...existingConfig.permissions,
263
+ allow: [...new Set([...existingAllow, ...templateAllow])],
264
+ deny: [...new Set([...existingDeny, ...templateDeny])],
265
+ };
266
+ await fs.writeFile(settingsPath, JSON.stringify(existingConfig, null, 2) + '\n', 'utf-8');
267
+ }
268
+ else {
269
+ await fs.writeFile(settingsPath, templateContent, 'utf-8');
270
+ }
271
+ }
249
272
  async function updateGitignore(cwd) {
250
273
  const gitignorePath = path.join(cwd, '.gitignore');
251
274
  let content = '';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import chalk from 'chalk';
2
3
  import { Command } from '@commander-js/extra-typings';
3
4
  import { readFileSync } from 'fs';
4
5
  import { join, dirname } from 'path';
@@ -16,7 +17,7 @@ const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'),
16
17
  const program = new Command();
17
18
  program
18
19
  .name('meltctl')
19
- .description('Set up AI-first development standards (AGENTS.md, Claude skills, Cursor commands, MCP config) in your project.\n\nRequires a @meltstudio.co Google Workspace account. Run `meltctl login` first, then `meltctl project init` in your repo.')
20
+ .description('Set up AI-first development standards (AGENTS.md, Claude skills, Cursor commands, MCP config) in your project.\n\nRequires a @meltstudio.co Google Workspace account. Run `npx @meltstudio/meltctl@latest login` first, then `npx @meltstudio/meltctl@latest project init` in your repo.')
20
21
  .version(packageJson.version)
21
22
  .addHelpText('beforeAll', () => {
22
23
  printBanner();
@@ -59,4 +60,8 @@ program
59
60
  console.log(packageJson.version);
60
61
  }
61
62
  });
62
- program.parseAsync(process.argv);
63
+ program.parseAsync(process.argv).catch((error) => {
64
+ const message = error instanceof Error ? error.message : 'An unexpected error occurred';
65
+ console.error(chalk.red(message));
66
+ process.exit(1);
67
+ });
@@ -35,10 +35,10 @@ export async function isAuthenticated() {
35
35
  export async function authenticatedFetch(urlPath) {
36
36
  const auth = await getStoredAuth();
37
37
  if (!auth) {
38
- throw new Error('Not authenticated. Run `meltctl login` first.');
38
+ throw new Error('Not authenticated. Run `npx @meltstudio/meltctl@latest login` first.');
39
39
  }
40
40
  if (new Date(auth.expiresAt) <= new Date()) {
41
- throw new Error('Session expired. Run `meltctl login` to re-authenticate.');
41
+ throw new Error('Session expired. Run `npx @meltstudio/meltctl@latest login` to re-authenticate.');
42
42
  }
43
43
  const response = await fetch(`${API_BASE}${urlPath}`, {
44
44
  headers: {
@@ -46,7 +46,7 @@ export async function authenticatedFetch(urlPath) {
46
46
  },
47
47
  });
48
48
  if (response.status === 401) {
49
- throw new Error('Session expired. Run `meltctl login` to re-authenticate.');
49
+ throw new Error('Session expired. Run `npx @meltstudio/meltctl@latest login` to re-authenticate.');
50
50
  }
51
51
  return response;
52
52
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meltstudio/meltctl",
3
- "version": "4.6.0",
3
+ "version": "4.6.2",
4
4
  "description": "AI-first development tools for teams - set up AGENTS.md, Claude Code, Cursor, and Copilot standards",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",