@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.
- package/dist/commands/init.js +31 -8
- package/dist/index.js +7 -2
- package/dist/utils/auth.js +3 -3
- package/package.json +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
+
});
|
package/dist/utils/auth.js
CHANGED
|
@@ -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