@meltstudio/meltctl 2.4.1 → 4.0.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.
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +149 -0
- package/dist/commands/login.d.ts +1 -0
- package/dist/commands/login.js +90 -0
- package/dist/commands/logout.d.ts +1 -0
- package/dist/commands/logout.js +12 -0
- package/dist/index.js +28 -31
- package/dist/utils/auth.d.ts +12 -0
- package/dist/utils/auth.js +52 -0
- package/dist/utils/banner.d.ts +1 -0
- package/dist/utils/banner.js +22 -0
- package/package.json +8 -7
- package/dist/commands/project/clean.d.ts +0 -5
- package/dist/commands/project/clean.js +0 -243
- package/dist/commands/project/init.d.ts +0 -5
- package/dist/commands/project/init.js +0 -158
- package/dist/commands/project/update.d.ts +0 -1
- package/dist/commands/project/update.js +0 -107
- package/templates/cursor-commands/melt-complete.md +0 -55
- package/templates/cursor-commands/melt-debug.md +0 -43
- package/templates/cursor-commands/melt-docs.md +0 -44
- package/templates/cursor-commands/melt-implement.md +0 -43
- package/templates/cursor-commands/melt-plan.md +0 -60
- package/templates/cursor-commands/melt-pr.md +0 -53
- package/templates/cursor-commands/melt-review.md +0 -43
- package/templates/cursor-commands/melt-test-plan.md +0 -50
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { authenticatedFetch, isAuthenticated } from '../utils/auth.js';
|
|
5
|
+
const SKILL_FRONTMATTER = {
|
|
6
|
+
plan: `---
|
|
7
|
+
user-invocable: true
|
|
8
|
+
description: Design an implementation approach before writing code
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
`,
|
|
12
|
+
review: `---
|
|
13
|
+
user-invocable: true
|
|
14
|
+
description: Review changes against project standards
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
`,
|
|
18
|
+
pr: `---
|
|
19
|
+
user-invocable: true
|
|
20
|
+
description: Create a well-structured pull request
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
`,
|
|
24
|
+
debug: `---
|
|
25
|
+
user-invocable: true
|
|
26
|
+
description: Systematically investigate and fix bugs
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
`,
|
|
30
|
+
};
|
|
31
|
+
const GITIGNORE_ENTRIES = ['.env.local', '.claude/settings.local.json'];
|
|
32
|
+
async function fetchTemplates() {
|
|
33
|
+
const response = await authenticatedFetch('/templates');
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
throw new Error(`Failed to fetch templates: ${response.statusText}`);
|
|
36
|
+
}
|
|
37
|
+
const data = (await response.json());
|
|
38
|
+
return data.files;
|
|
39
|
+
}
|
|
40
|
+
export async function initCommand(options) {
|
|
41
|
+
const cwd = process.cwd();
|
|
42
|
+
// Check if already initialized (AGENTS.md exists)
|
|
43
|
+
if (!options.force && (await fs.pathExists(path.join(cwd, 'AGENTS.md')))) {
|
|
44
|
+
console.log(chalk.yellow('Project already has an AGENTS.md file. Use --force to overwrite.'));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
// Require authentication
|
|
48
|
+
if (!(await isAuthenticated())) {
|
|
49
|
+
console.error(chalk.red('Not authenticated. Run `meltctl login` first.'));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
let templates;
|
|
53
|
+
try {
|
|
54
|
+
templates = await fetchTemplates();
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error instanceof Error && error.message.includes('expired')) {
|
|
58
|
+
console.error(chalk.red('Session expired. Run `meltctl login` to re-authenticate.'));
|
|
59
|
+
}
|
|
60
|
+
else if (error instanceof Error && error.message.includes('fetch')) {
|
|
61
|
+
console.error(chalk.red('Could not reach Melt API. Check your connection.'));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
console.error(chalk.red(`Failed to fetch templates: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
65
|
+
}
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
console.log(chalk.bold('Initializing Melt development tools...'));
|
|
69
|
+
console.log();
|
|
70
|
+
// Copy AGENTS.md
|
|
71
|
+
const agentsMd = templates['agents-md.md'];
|
|
72
|
+
if (agentsMd) {
|
|
73
|
+
await fs.writeFile(path.join(cwd, 'AGENTS.md'), agentsMd, 'utf-8');
|
|
74
|
+
}
|
|
75
|
+
// Copy .claude/settings.json
|
|
76
|
+
const claudeSettings = templates['claude-settings.json'];
|
|
77
|
+
if (claudeSettings) {
|
|
78
|
+
await fs.ensureDir(path.join(cwd, '.claude'));
|
|
79
|
+
await fs.writeFile(path.join(cwd, '.claude/settings.json'), claudeSettings, 'utf-8');
|
|
80
|
+
}
|
|
81
|
+
// Create Claude skills from workflow templates
|
|
82
|
+
const workflows = ['plan', 'review', 'pr', 'debug'];
|
|
83
|
+
for (const name of workflows) {
|
|
84
|
+
const workflowContent = templates[`workflows/${name}.md`];
|
|
85
|
+
if (workflowContent) {
|
|
86
|
+
const skillDir = path.join(cwd, `.claude/skills/melt-${name}`);
|
|
87
|
+
await fs.ensureDir(skillDir);
|
|
88
|
+
const skillContent = SKILL_FRONTMATTER[name] + workflowContent;
|
|
89
|
+
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillContent, 'utf-8');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Copy .cursor/rules/standards.mdc
|
|
93
|
+
const cursorRules = templates['cursor-rules.mdc'];
|
|
94
|
+
if (cursorRules) {
|
|
95
|
+
await fs.ensureDir(path.join(cwd, '.cursor/rules'));
|
|
96
|
+
await fs.writeFile(path.join(cwd, '.cursor/rules/standards.mdc'), cursorRules, 'utf-8');
|
|
97
|
+
}
|
|
98
|
+
// Copy Cursor commands from workflow templates
|
|
99
|
+
await fs.ensureDir(path.join(cwd, '.cursor/commands'));
|
|
100
|
+
for (const name of workflows) {
|
|
101
|
+
const workflowContent = templates[`workflows/${name}.md`];
|
|
102
|
+
if (workflowContent) {
|
|
103
|
+
await fs.writeFile(path.join(cwd, `.cursor/commands/melt-${name}.md`), workflowContent, 'utf-8');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Copy .mcp.json
|
|
107
|
+
const mcpConfig = templates['mcp-configs/base.json'];
|
|
108
|
+
if (mcpConfig) {
|
|
109
|
+
await fs.writeFile(path.join(cwd, '.mcp.json'), mcpConfig, 'utf-8');
|
|
110
|
+
}
|
|
111
|
+
// Copy .env.melt.example
|
|
112
|
+
const envExample = templates['env-melt-example'];
|
|
113
|
+
if (envExample) {
|
|
114
|
+
await fs.writeFile(path.join(cwd, '.env.melt.example'), envExample, 'utf-8');
|
|
115
|
+
}
|
|
116
|
+
// Update .gitignore
|
|
117
|
+
await updateGitignore(cwd);
|
|
118
|
+
// Print summary
|
|
119
|
+
console.log(chalk.green('Created files:'));
|
|
120
|
+
console.log(chalk.dim(' AGENTS.md'));
|
|
121
|
+
console.log(chalk.dim(' .claude/settings.json'));
|
|
122
|
+
console.log(chalk.dim(' .claude/skills/melt-{plan,review,pr,debug}/SKILL.md'));
|
|
123
|
+
console.log(chalk.dim(' .cursor/rules/standards.mdc'));
|
|
124
|
+
console.log(chalk.dim(' .cursor/commands/melt-{plan,review,pr,debug}.md'));
|
|
125
|
+
console.log(chalk.dim(' .mcp.json'));
|
|
126
|
+
console.log(chalk.dim(' .env.melt.example'));
|
|
127
|
+
console.log();
|
|
128
|
+
console.log(chalk.yellow('Next steps:'));
|
|
129
|
+
console.log(chalk.dim(' 1. Edit AGENTS.md to describe your project and add team-specific standards'));
|
|
130
|
+
console.log(chalk.dim(' 2. Copy .env.melt.example to .env.local and fill in credentials'));
|
|
131
|
+
console.log(chalk.dim(' 3. Commit the generated files'));
|
|
132
|
+
console.log();
|
|
133
|
+
console.log(chalk.green('Done!'));
|
|
134
|
+
}
|
|
135
|
+
async function updateGitignore(cwd) {
|
|
136
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
137
|
+
let content = '';
|
|
138
|
+
if (await fs.pathExists(gitignorePath)) {
|
|
139
|
+
content = await fs.readFile(gitignorePath, 'utf-8');
|
|
140
|
+
}
|
|
141
|
+
const missingEntries = GITIGNORE_ENTRIES.filter(entry => !content.includes(entry));
|
|
142
|
+
if (missingEntries.length > 0) {
|
|
143
|
+
const suffix = content.endsWith('\n') || content === '' ? '' : '\n';
|
|
144
|
+
const section = missingEntries.length > 0
|
|
145
|
+
? `${suffix}\n# Melt - local settings\n${missingEntries.join('\n')}\n`
|
|
146
|
+
: '';
|
|
147
|
+
await fs.writeFile(gitignorePath, content + section, 'utf-8');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loginCommand(): Promise<void>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import { URL } from 'url';
|
|
4
|
+
import { exec } from 'child_process';
|
|
5
|
+
import { API_BASE, storeAuth } from '../utils/auth.js';
|
|
6
|
+
import { printBanner } from '../utils/banner.js';
|
|
7
|
+
const LOGIN_TIMEOUT_MS = 60_000;
|
|
8
|
+
function openBrowser(url) {
|
|
9
|
+
const command = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
10
|
+
exec(`${command} "${url}"`);
|
|
11
|
+
}
|
|
12
|
+
function findFreePort() {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const server = http.createServer();
|
|
15
|
+
server.listen(0, () => {
|
|
16
|
+
const address = server.address();
|
|
17
|
+
if (address && typeof address === 'object') {
|
|
18
|
+
const port = address.port;
|
|
19
|
+
server.close(() => resolve(port));
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
reject(new Error('Could not find free port'));
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export async function loginCommand() {
|
|
28
|
+
printBanner();
|
|
29
|
+
console.log(chalk.bold(' Logging in to Melt...'));
|
|
30
|
+
const port = await findFreePort();
|
|
31
|
+
const redirectUri = `http://localhost:${port.toString()}`;
|
|
32
|
+
const authCode = await new Promise((resolve, reject) => {
|
|
33
|
+
const timeout = setTimeout(() => {
|
|
34
|
+
server.close();
|
|
35
|
+
reject(new Error('Authentication timed out. Please try again.'));
|
|
36
|
+
}, LOGIN_TIMEOUT_MS);
|
|
37
|
+
const server = http.createServer((req, res) => {
|
|
38
|
+
const url = new URL(req.url ?? '/', `http://localhost:${port.toString()}`);
|
|
39
|
+
const code = url.searchParams.get('code');
|
|
40
|
+
const error = url.searchParams.get('error');
|
|
41
|
+
if (error) {
|
|
42
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
43
|
+
res.end('<html><body><h2>Authentication failed</h2><p>You can close this tab.</p></body></html>');
|
|
44
|
+
clearTimeout(timeout);
|
|
45
|
+
server.close();
|
|
46
|
+
reject(new Error(`Authentication denied: ${error}`));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (!code) {
|
|
50
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
51
|
+
res.end('<html><body><h2>Missing authorization code</h2></body></html>');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
55
|
+
res.end('<html><body><h2>Authentication successful!</h2><p>You can close this tab and return to your terminal.</p></body></html>');
|
|
56
|
+
clearTimeout(timeout);
|
|
57
|
+
server.close();
|
|
58
|
+
resolve(code);
|
|
59
|
+
});
|
|
60
|
+
server.listen(port, () => {
|
|
61
|
+
const authUrl = `${API_BASE}/auth/google?redirect_uri=${encodeURIComponent(redirectUri)}`;
|
|
62
|
+
console.log(chalk.dim('Opening browser for authentication...'));
|
|
63
|
+
openBrowser(authUrl);
|
|
64
|
+
console.log(chalk.dim(`If the browser doesn't open, visit: ${authUrl}`));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
console.log(chalk.dim('Exchanging authorization code...'));
|
|
68
|
+
const response = await fetch(`${API_BASE}/auth/token`, {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
headers: { 'Content-Type': 'application/json' },
|
|
71
|
+
body: JSON.stringify({ code: authCode, redirect_uri: redirectUri }),
|
|
72
|
+
});
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const error = (await response.json());
|
|
75
|
+
if (response.status === 403) {
|
|
76
|
+
console.error(chalk.red('Only @meltstudio.co accounts can use this tool.'));
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
console.error(chalk.red(`Authentication failed: ${error.error ?? 'Unknown error'}`));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
const data = (await response.json());
|
|
83
|
+
await storeAuth({
|
|
84
|
+
token: data.token,
|
|
85
|
+
email: data.email,
|
|
86
|
+
expiresAt: data.expiresAt,
|
|
87
|
+
});
|
|
88
|
+
console.log();
|
|
89
|
+
console.log(chalk.green(`Logged in as ${data.email}`));
|
|
90
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function logoutCommand(): Promise<void>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { clearAuth, getStoredAuth } from '../utils/auth.js';
|
|
3
|
+
export async function logoutCommand() {
|
|
4
|
+
const auth = await getStoredAuth();
|
|
5
|
+
await clearAuth();
|
|
6
|
+
if (auth) {
|
|
7
|
+
console.log(chalk.green(`Logged out (was ${auth.email})`));
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
console.log(chalk.dim('No active session found.'));
|
|
11
|
+
}
|
|
12
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,10 @@ import { Command } from '@commander-js/extra-typings';
|
|
|
3
3
|
import { readFileSync } from 'fs';
|
|
4
4
|
import { join, dirname } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
|
-
import { initCommand } from './commands/
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { initCommand } from './commands/init.js';
|
|
7
|
+
import { loginCommand } from './commands/login.js';
|
|
8
|
+
import { logoutCommand } from './commands/logout.js';
|
|
9
|
+
import { printBanner } from './utils/banner.js';
|
|
9
10
|
import { checkAndEnforceUpdate } from './utils/version-check.js';
|
|
10
11
|
import { versionCheckCommand } from './commands/version.js';
|
|
11
12
|
// Read version from package.json
|
|
@@ -15,43 +16,39 @@ const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'),
|
|
|
15
16
|
const program = new Command();
|
|
16
17
|
program
|
|
17
18
|
.name('meltctl')
|
|
18
|
-
.description('
|
|
19
|
+
.description('Set up AI-first development standards (AGENTS.md, Claude skills, Cursor rules, MCP config) in your project.\n\nRequires a @meltstudio.co Google Workspace account. Run `meltctl login` first, then `meltctl project init` in your repo.')
|
|
19
20
|
.version(packageJson.version)
|
|
21
|
+
.addHelpText('beforeAll', () => {
|
|
22
|
+
printBanner();
|
|
23
|
+
return '';
|
|
24
|
+
})
|
|
20
25
|
.hook('preAction', async () => {
|
|
21
26
|
await checkAndEnforceUpdate();
|
|
22
27
|
});
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
.
|
|
26
|
-
.
|
|
27
|
-
|
|
28
|
-
.command('init')
|
|
29
|
-
.description('Initialize project with Melt development tools')
|
|
30
|
-
.option('--shell <type>', 'specify shell type (sh|ps)', 'auto')
|
|
31
|
-
.action(options => {
|
|
32
|
-
// Validate shell option
|
|
33
|
-
const validShells = ['sh', 'ps', 'auto'];
|
|
34
|
-
const shell = validShells.includes(options.shell)
|
|
35
|
-
? options.shell
|
|
36
|
-
: 'auto';
|
|
37
|
-
return initCommand({ shell });
|
|
28
|
+
program
|
|
29
|
+
.command('login')
|
|
30
|
+
.description('authenticate with Google Workspace (opens browser)')
|
|
31
|
+
.action(async () => {
|
|
32
|
+
await loginCommand();
|
|
38
33
|
});
|
|
39
|
-
|
|
40
|
-
.command('
|
|
41
|
-
.description('
|
|
42
|
-
.action(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
program
|
|
35
|
+
.command('logout')
|
|
36
|
+
.description('clear stored credentials from ~/.meltctl/')
|
|
37
|
+
.action(async () => {
|
|
38
|
+
await logoutCommand();
|
|
39
|
+
});
|
|
40
|
+
const project = program.command('project').description('manage project configuration');
|
|
41
|
+
project
|
|
42
|
+
.command('init')
|
|
43
|
+
.description('scaffold Melt development tools into the current directory (AGENTS.md, .claude/, .cursor/, .mcp.json)')
|
|
44
|
+
.option('--force', 'overwrite existing files if already initialized')
|
|
47
45
|
.action(options => {
|
|
48
|
-
return
|
|
46
|
+
return initCommand({ force: options.force });
|
|
49
47
|
});
|
|
50
|
-
// Version check command
|
|
51
48
|
program
|
|
52
49
|
.command('version')
|
|
53
|
-
.description('
|
|
54
|
-
.option('--check', 'check for updates')
|
|
50
|
+
.description('show current version')
|
|
51
|
+
.option('--check', 'also check for available updates')
|
|
55
52
|
.action(async (options) => {
|
|
56
53
|
if (options.check) {
|
|
57
54
|
await versionCheckCommand();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const API_BASE: string;
|
|
2
|
+
interface StoredAuth {
|
|
3
|
+
token: string;
|
|
4
|
+
email: string;
|
|
5
|
+
expiresAt: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function getStoredAuth(): Promise<StoredAuth | undefined>;
|
|
8
|
+
export declare function storeAuth(auth: StoredAuth): Promise<void>;
|
|
9
|
+
export declare function clearAuth(): Promise<void>;
|
|
10
|
+
export declare function isAuthenticated(): Promise<boolean>;
|
|
11
|
+
export declare function authenticatedFetch(urlPath: string): Promise<Response>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
const AUTH_DIR = path.join(os.homedir(), '.meltctl');
|
|
5
|
+
const AUTH_FILE = path.join(AUTH_DIR, 'auth.json');
|
|
6
|
+
export const API_BASE = process.env['MELTCTL_API_URL'] ??
|
|
7
|
+
'https://ewszkw32he2ebgkwlbwmoubf5y0hllpx.lambda-url.us-east-1.on.aws';
|
|
8
|
+
export async function getStoredAuth() {
|
|
9
|
+
if (!(await fs.pathExists(AUTH_FILE))) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
return (await fs.readJson(AUTH_FILE));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function storeAuth(auth) {
|
|
20
|
+
await fs.ensureDir(AUTH_DIR);
|
|
21
|
+
await fs.writeJson(AUTH_FILE, auth, { spaces: 2 });
|
|
22
|
+
}
|
|
23
|
+
export async function clearAuth() {
|
|
24
|
+
if (await fs.pathExists(AUTH_FILE)) {
|
|
25
|
+
await fs.remove(AUTH_FILE);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function isAuthenticated() {
|
|
29
|
+
const auth = await getStoredAuth();
|
|
30
|
+
if (!auth) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return new Date(auth.expiresAt) > new Date();
|
|
34
|
+
}
|
|
35
|
+
export async function authenticatedFetch(urlPath) {
|
|
36
|
+
const auth = await getStoredAuth();
|
|
37
|
+
if (!auth) {
|
|
38
|
+
throw new Error('Not authenticated. Run `meltctl login` first.');
|
|
39
|
+
}
|
|
40
|
+
if (new Date(auth.expiresAt) <= new Date()) {
|
|
41
|
+
throw new Error('Session expired. Run `meltctl login` to re-authenticate.');
|
|
42
|
+
}
|
|
43
|
+
const response = await fetch(`${API_BASE}${urlPath}`, {
|
|
44
|
+
headers: {
|
|
45
|
+
Authorization: `Bearer ${auth.token}`,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
if (response.status === 401) {
|
|
49
|
+
throw new Error('Session expired. Run `meltctl login` to re-authenticate.');
|
|
50
|
+
}
|
|
51
|
+
return response;
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function printBanner(): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import gradient from 'gradient-string';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
import { join, dirname } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8'));
|
|
8
|
+
// Melt brand colors: yellow, magenta/pink, cyan, red, orange
|
|
9
|
+
const meltGradient = gradient(['#FFC107', '#E91E63', '#00BCD4', '#FF5722']);
|
|
10
|
+
const LOGO = `
|
|
11
|
+
███╗ ███╗███████╗██╗ ████████╗ ██████╗████████╗██╗
|
|
12
|
+
████╗ ████║██╔════╝██║ ╚══██╔══╝██╔════╝╚══██╔══╝██║
|
|
13
|
+
██╔████╔██║█████╗ ██║ ██║ ██║ ██║ ██║
|
|
14
|
+
██║╚██╔╝██║██╔══╝ ██║ ██║ ██║ ██║ ██║
|
|
15
|
+
██║ ╚═╝ ██║███████╗███████╗██║ ╚██████╗ ██║ ███████╗
|
|
16
|
+
╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚══════╝`;
|
|
17
|
+
export function printBanner() {
|
|
18
|
+
console.log(meltGradient(LOGO));
|
|
19
|
+
console.log();
|
|
20
|
+
console.log(` ${chalk.dim('v' + pkg.version)} ${chalk.dim('·')} ${chalk.dim('AI-first development tools for teams')}`);
|
|
21
|
+
console.log();
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meltstudio/meltctl",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "4.0.0",
|
|
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",
|
|
7
7
|
"bin": {
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist/**/*",
|
|
12
|
-
"templates/**/*",
|
|
13
12
|
"README.md",
|
|
14
13
|
"LICENSE"
|
|
15
14
|
],
|
|
@@ -37,7 +36,10 @@
|
|
|
37
36
|
"keywords": [
|
|
38
37
|
"cli",
|
|
39
38
|
"development-tools",
|
|
39
|
+
"agents-md",
|
|
40
|
+
"claude-code",
|
|
40
41
|
"cursor",
|
|
42
|
+
"copilot",
|
|
41
43
|
"ai-development",
|
|
42
44
|
"melt",
|
|
43
45
|
"automation"
|
|
@@ -45,12 +47,11 @@
|
|
|
45
47
|
"author": "Melt Studio",
|
|
46
48
|
"license": "MIT",
|
|
47
49
|
"dependencies": {
|
|
48
|
-
"@clack/prompts": "^0.9.0",
|
|
49
|
-
"commander": "^12.1.0",
|
|
50
50
|
"@commander-js/extra-typings": "^12.1.0",
|
|
51
51
|
"chalk": "^5.4.1",
|
|
52
|
-
"
|
|
53
|
-
"fs-extra": "^11.2.0"
|
|
52
|
+
"commander": "^12.1.0",
|
|
53
|
+
"fs-extra": "^11.2.0",
|
|
54
|
+
"gradient-string": "^3.0.0"
|
|
54
55
|
},
|
|
55
56
|
"devDependencies": {
|
|
56
57
|
"@types/fs-extra": "^11.0.4",
|