@meltstudio/meltctl 3.0.0 → 4.1.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.js +63 -16
- 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 +25 -5
- 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 +4 -4
- package/templates/agents-md.md +0 -117
- package/templates/claude-settings.json +0 -36
- package/templates/cursor-rules.mdc +0 -39
- package/templates/env-melt-example +0 -23
- package/templates/mcp-configs/base.json +0 -8
- package/templates/workflows/debug.md +0 -32
- package/templates/workflows/plan.md +0 -32
- package/templates/workflows/pr.md +0 -28
- package/templates/workflows/review.md +0 -28
package/dist/commands/init.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import {
|
|
5
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const TEMPLATES_DIR = path.join(__dirname, '../../templates');
|
|
4
|
+
import { authenticatedFetch, isAuthenticated } from '../utils/auth.js';
|
|
7
5
|
const SKILL_FRONTMATTER = {
|
|
8
6
|
plan: `---
|
|
9
7
|
user-invocable: true
|
|
@@ -31,6 +29,14 @@ description: Systematically investigate and fix bugs
|
|
|
31
29
|
`,
|
|
32
30
|
};
|
|
33
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
|
+
}
|
|
34
40
|
export async function initCommand(options) {
|
|
35
41
|
const cwd = process.cwd();
|
|
36
42
|
// Check if already initialized (AGENTS.md exists)
|
|
@@ -38,34 +44,75 @@ export async function initCommand(options) {
|
|
|
38
44
|
console.log(chalk.yellow('Project already has an AGENTS.md file. Use --force to overwrite.'));
|
|
39
45
|
process.exit(1);
|
|
40
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
|
+
}
|
|
41
68
|
console.log(chalk.bold('Initializing Melt development tools...'));
|
|
42
69
|
console.log();
|
|
43
70
|
// Copy AGENTS.md
|
|
44
|
-
|
|
71
|
+
const agentsMd = templates['agents-md.md'];
|
|
72
|
+
if (agentsMd) {
|
|
73
|
+
await fs.writeFile(path.join(cwd, 'AGENTS.md'), agentsMd, 'utf-8');
|
|
74
|
+
}
|
|
45
75
|
// Copy .claude/settings.json
|
|
46
|
-
|
|
47
|
-
|
|
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
|
+
}
|
|
48
81
|
// Create Claude skills from workflow templates
|
|
49
82
|
const workflows = ['plan', 'review', 'pr', 'debug'];
|
|
50
83
|
for (const name of workflows) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
+
}
|
|
56
91
|
}
|
|
57
92
|
// Copy .cursor/rules/standards.mdc
|
|
58
|
-
|
|
59
|
-
|
|
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
|
+
}
|
|
60
98
|
// Copy Cursor commands from workflow templates
|
|
61
99
|
await fs.ensureDir(path.join(cwd, '.cursor/commands'));
|
|
62
100
|
for (const name of workflows) {
|
|
63
|
-
|
|
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
|
+
}
|
|
64
105
|
}
|
|
65
106
|
// Copy .mcp.json
|
|
66
|
-
|
|
107
|
+
const mcpConfig = templates['mcp-configs/base.json'];
|
|
108
|
+
if (mcpConfig) {
|
|
109
|
+
await fs.writeFile(path.join(cwd, '.mcp.json'), mcpConfig, 'utf-8');
|
|
110
|
+
}
|
|
67
111
|
// Copy .env.melt.example
|
|
68
|
-
|
|
112
|
+
const envExample = templates['env-melt-example'];
|
|
113
|
+
if (envExample) {
|
|
114
|
+
await fs.writeFile(path.join(cwd, '.env.melt.example'), envExample, 'utf-8');
|
|
115
|
+
}
|
|
69
116
|
// Update .gitignore
|
|
70
117
|
await updateGitignore(cwd);
|
|
71
118
|
// Print summary
|
|
@@ -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
|
@@ -4,6 +4,9 @@ import { readFileSync } from 'fs';
|
|
|
4
4
|
import { join, dirname } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
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';
|
|
7
10
|
import { checkAndEnforceUpdate } from './utils/version-check.js';
|
|
8
11
|
import { versionCheckCommand } from './commands/version.js';
|
|
9
12
|
// Read version from package.json
|
|
@@ -13,22 +16,39 @@ const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'),
|
|
|
13
16
|
const program = new Command();
|
|
14
17
|
program
|
|
15
18
|
.name('meltctl')
|
|
16
|
-
.description('AI-first development
|
|
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.')
|
|
17
20
|
.version(packageJson.version)
|
|
21
|
+
.addHelpText('beforeAll', () => {
|
|
22
|
+
printBanner();
|
|
23
|
+
return '';
|
|
24
|
+
})
|
|
18
25
|
.hook('preAction', async () => {
|
|
19
26
|
await checkAndEnforceUpdate();
|
|
20
27
|
});
|
|
21
28
|
program
|
|
29
|
+
.command('login')
|
|
30
|
+
.description('authenticate with Google Workspace (opens browser)')
|
|
31
|
+
.action(async () => {
|
|
32
|
+
await loginCommand();
|
|
33
|
+
});
|
|
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
|
|
22
42
|
.command('init')
|
|
23
|
-
.description('
|
|
24
|
-
.option('--force', 'overwrite existing files')
|
|
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')
|
|
25
45
|
.action(options => {
|
|
26
46
|
return initCommand({ force: options.force });
|
|
27
47
|
});
|
|
28
48
|
program
|
|
29
49
|
.command('version')
|
|
30
|
-
.description('
|
|
31
|
-
.option('--check', 'check for updates')
|
|
50
|
+
.description('show current version')
|
|
51
|
+
.option('--check', 'also check for available updates')
|
|
32
52
|
.action(async (options) => {
|
|
33
53
|
if (options.check) {
|
|
34
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meltstudio/meltctl",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
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",
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist/**/*",
|
|
12
|
-
"templates/**/*",
|
|
13
12
|
"README.md",
|
|
14
13
|
"LICENSE"
|
|
15
14
|
],
|
|
@@ -48,10 +47,11 @@
|
|
|
48
47
|
"author": "Melt Studio",
|
|
49
48
|
"license": "MIT",
|
|
50
49
|
"dependencies": {
|
|
51
|
-
"commander": "^12.1.0",
|
|
52
50
|
"@commander-js/extra-typings": "^12.1.0",
|
|
53
51
|
"chalk": "^5.4.1",
|
|
54
|
-
"
|
|
52
|
+
"commander": "^12.1.0",
|
|
53
|
+
"fs-extra": "^11.2.0",
|
|
54
|
+
"gradient-string": "^3.0.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/fs-extra": "^11.0.4",
|
package/templates/agents-md.md
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
# AGENTS.md
|
|
2
|
-
|
|
3
|
-
## Project Overview
|
|
4
|
-
|
|
5
|
-
<!-- Describe your project here: what it does, key technologies, and architecture overview -->
|
|
6
|
-
|
|
7
|
-
## Architecture Standards
|
|
8
|
-
|
|
9
|
-
### Important: Respect Existing Code
|
|
10
|
-
|
|
11
|
-
Before applying any standards below, **understand the existing project structure first**. If the project already follows a different architecture or pattern, continue following the existing conventions. These standards apply to new code in greenfield areas - never refactor or restructure existing code to match these guidelines unless explicitly asked.
|
|
12
|
-
|
|
13
|
-
### Recommended Structure (for new projects/domains)
|
|
14
|
-
|
|
15
|
-
For new frontend code, prefer domain-driven architecture:
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
src/
|
|
19
|
-
domains/
|
|
20
|
-
<domain>/
|
|
21
|
-
components/ # UI components scoped to this domain
|
|
22
|
-
hooks/ # Custom hooks for this domain
|
|
23
|
-
services/ # API calls and business logic
|
|
24
|
-
types/ # TypeScript types and interfaces
|
|
25
|
-
utils/ # Domain-specific utilities
|
|
26
|
-
shared/
|
|
27
|
-
components/ # Shared UI components
|
|
28
|
-
hooks/ # Shared custom hooks
|
|
29
|
-
services/ # Shared API services
|
|
30
|
-
types/ # Shared TypeScript types
|
|
31
|
-
utils/ # Shared utilities
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
- Each domain is self-contained with its own components, hooks, services, types, and utils
|
|
35
|
-
- Cross-domain imports go through the `shared/` directory
|
|
36
|
-
- Never import directly between domains
|
|
37
|
-
|
|
38
|
-
If the project uses a different structure, follow the existing patterns instead.
|
|
39
|
-
|
|
40
|
-
### TypeScript Standards
|
|
41
|
-
- Strict mode enabled (`strict: true` in tsconfig)
|
|
42
|
-
- No `any` types - use `unknown` with type guards when the type is truly unknown
|
|
43
|
-
- Validate external data with Zod schemas at system boundaries
|
|
44
|
-
- Export types from dedicated `types.ts` files
|
|
45
|
-
|
|
46
|
-
### Code Quality
|
|
47
|
-
- Minimum 80% test coverage for new code
|
|
48
|
-
- Use conventional commits (feat:, fix:, chore:, docs:, refactor:, test:)
|
|
49
|
-
- No eslint-disable comments without team approval
|
|
50
|
-
- All functions must have explicit return types
|
|
51
|
-
- Prefer composition over inheritance
|
|
52
|
-
|
|
53
|
-
## Agent Permissions
|
|
54
|
-
|
|
55
|
-
### Automatically Approved (No Confirmation Needed)
|
|
56
|
-
- Reading any file in the repository
|
|
57
|
-
- Running tests, linters, type checkers, and formatters
|
|
58
|
-
- Git read operations (status, log, diff, branch)
|
|
59
|
-
- Searching code and exploring the codebase
|
|
60
|
-
- Package dependency lookups
|
|
61
|
-
|
|
62
|
-
### Requires Approval
|
|
63
|
-
- Writing or modifying files
|
|
64
|
-
- Git write operations (add, commit, checkout, branch creation)
|
|
65
|
-
- Running build commands
|
|
66
|
-
- Installing or removing dependencies
|
|
67
|
-
- Creating pull requests or issues
|
|
68
|
-
|
|
69
|
-
### Blocked (Never Execute)
|
|
70
|
-
- `git push --force` or `git push --force-with-lease`
|
|
71
|
-
- `git reset --hard`
|
|
72
|
-
- `rm -rf /` or any recursive delete of root paths
|
|
73
|
-
- Force push to main or master branches
|
|
74
|
-
- Dropping database tables in production
|
|
75
|
-
- Modifying CI/CD pipeline files without explicit request
|
|
76
|
-
|
|
77
|
-
## Tool Access
|
|
78
|
-
|
|
79
|
-
### CLI Tools
|
|
80
|
-
- Use `gh` CLI for GitHub operations (PRs, issues, releases)
|
|
81
|
-
- Use project's package manager (check for yarn.lock, pnpm-lock.yaml, or package-lock.json)
|
|
82
|
-
- Run tests with the project's configured test runner
|
|
83
|
-
|
|
84
|
-
### Browser (via DevTools MCP)
|
|
85
|
-
- Use Chrome DevTools MCP server for browser testing when available
|
|
86
|
-
- Inspect elements, check console errors, verify visual output
|
|
87
|
-
- Test responsive layouts and interactions
|
|
88
|
-
|
|
89
|
-
### Databases
|
|
90
|
-
- Read queries are safe to execute
|
|
91
|
-
- Write queries require approval
|
|
92
|
-
- Schema changes require explicit request and approval
|
|
93
|
-
|
|
94
|
-
## Project Management
|
|
95
|
-
|
|
96
|
-
- Reference issue/ticket IDs in commit messages and PR descriptions
|
|
97
|
-
- Follow the team's workflow for issue progression
|
|
98
|
-
- Update ticket status when starting and completing work
|
|
99
|
-
|
|
100
|
-
## Workflow Skills
|
|
101
|
-
|
|
102
|
-
The following workflows are available as AI skills:
|
|
103
|
-
|
|
104
|
-
- **plan** - Gather requirements, explore codebase, design approach, present plan for approval
|
|
105
|
-
- **review** - Review changes against these standards, categorize findings
|
|
106
|
-
- **pr** - Analyze changes, draft PR description, run pre-flight checks, create PR
|
|
107
|
-
- **debug** - Investigate issues, isolate root cause, write regression test, fix
|
|
108
|
-
|
|
109
|
-
## Customization
|
|
110
|
-
|
|
111
|
-
<!-- Add project-specific standards below this line -->
|
|
112
|
-
<!-- Examples of things to add: -->
|
|
113
|
-
<!-- - API conventions (REST patterns, error formats, pagination) -->
|
|
114
|
-
<!-- - Component patterns (naming, file structure, state management) -->
|
|
115
|
-
<!-- - Database rules (naming conventions, migration patterns) -->
|
|
116
|
-
<!-- - Testing patterns (what to mock, integration test setup) -->
|
|
117
|
-
<!-- - Deployment rules (environment-specific concerns) -->
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(git status*)",
|
|
5
|
-
"Bash(git log*)",
|
|
6
|
-
"Bash(git diff*)",
|
|
7
|
-
"Bash(git branch*)",
|
|
8
|
-
"Bash(git show*)",
|
|
9
|
-
"Bash(npm test*)",
|
|
10
|
-
"Bash(yarn test*)",
|
|
11
|
-
"Bash(pnpm test*)",
|
|
12
|
-
"Bash(npx vitest*)",
|
|
13
|
-
"Bash(npx jest*)",
|
|
14
|
-
"Bash(npm run lint*)",
|
|
15
|
-
"Bash(yarn lint*)",
|
|
16
|
-
"Bash(pnpm lint*)",
|
|
17
|
-
"Bash(npm run build*)",
|
|
18
|
-
"Bash(yarn build*)",
|
|
19
|
-
"Bash(pnpm build*)",
|
|
20
|
-
"Bash(npx tsc*)",
|
|
21
|
-
"Bash(gh pr *)",
|
|
22
|
-
"Bash(gh issue *)",
|
|
23
|
-
"Read(*)"
|
|
24
|
-
],
|
|
25
|
-
"deny": [
|
|
26
|
-
"Bash(git push --force*)",
|
|
27
|
-
"Bash(git push * --force*)",
|
|
28
|
-
"Bash(git push --force-with-lease*)",
|
|
29
|
-
"Bash(git push * --force-with-lease*)",
|
|
30
|
-
"Bash(git reset --hard*)",
|
|
31
|
-
"Bash(rm -rf /*)",
|
|
32
|
-
"Bash(git push * main*)",
|
|
33
|
-
"Bash(git push * master*)"
|
|
34
|
-
]
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Melt development standards and architecture guidelines
|
|
3
|
-
globs:
|
|
4
|
-
alwaysApply: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Project Standards
|
|
8
|
-
|
|
9
|
-
## Important
|
|
10
|
-
- Always understand the existing project structure before making changes
|
|
11
|
-
- If the project follows different conventions than listed here, follow the existing patterns
|
|
12
|
-
- These standards apply to new greenfield code - never refactor existing code to match unless asked
|
|
13
|
-
|
|
14
|
-
## Architecture
|
|
15
|
-
- Prefer domain-driven architecture for new code: `src/domains/<domain>/{components,hooks,services,types,utils}`
|
|
16
|
-
- Cross-domain imports go through `src/shared/`
|
|
17
|
-
- Never import directly between domains
|
|
18
|
-
- Follow existing project patterns when they differ from the above
|
|
19
|
-
|
|
20
|
-
## TypeScript
|
|
21
|
-
- Strict mode enabled, no `any` types
|
|
22
|
-
- Validate external data with Zod at system boundaries
|
|
23
|
-
- All functions must have explicit return types
|
|
24
|
-
|
|
25
|
-
## Code Quality
|
|
26
|
-
- 80% minimum test coverage for new code
|
|
27
|
-
- Conventional commits (feat:, fix:, chore:, docs:, refactor:, test:)
|
|
28
|
-
- No eslint-disable comments without team approval
|
|
29
|
-
|
|
30
|
-
## Agent Behavior
|
|
31
|
-
- Read operations: auto-approved
|
|
32
|
-
- Write operations: require developer approval
|
|
33
|
-
- Destructive operations: blocked (no force push, no hard reset)
|
|
34
|
-
|
|
35
|
-
## Workflows
|
|
36
|
-
- Use `plan` before starting non-trivial implementations
|
|
37
|
-
- Use `review` to check changes against these standards
|
|
38
|
-
- Use `pr` to create well-structured pull requests
|
|
39
|
-
- Use `debug` for systematic bug investigation
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# Melt Development Tools - Environment Variables
|
|
2
|
-
# Copy this file to .env.local and fill in your values
|
|
3
|
-
# Never commit .env.local to version control
|
|
4
|
-
|
|
5
|
-
# --- Linear (if using Linear for project management) ---
|
|
6
|
-
# LINEAR_API_KEY=lin_api_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
7
|
-
|
|
8
|
-
# --- Jira (if using Jira for project management) ---
|
|
9
|
-
# JIRA_API_TOKEN=your-jira-api-token
|
|
10
|
-
# JIRA_BASE_URL=https://your-org.atlassian.net
|
|
11
|
-
# JIRA_USER_EMAIL=your-email@company.com
|
|
12
|
-
|
|
13
|
-
# --- Database (if agents need database access) ---
|
|
14
|
-
# DATABASE_URL=postgresql://user:password@localhost:5432/dbname
|
|
15
|
-
|
|
16
|
-
# --- AWS (if agents need AWS access) ---
|
|
17
|
-
# AWS_ACCESS_KEY_ID=your-access-key
|
|
18
|
-
# AWS_SECRET_ACCESS_KEY=your-secret-key
|
|
19
|
-
# AWS_REGION=us-east-1
|
|
20
|
-
|
|
21
|
-
# --- Other Integrations ---
|
|
22
|
-
# OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
23
|
-
# ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# Debug
|
|
2
|
-
|
|
3
|
-
Investigate and fix bugs systematically.
|
|
4
|
-
|
|
5
|
-
## Instructions
|
|
6
|
-
|
|
7
|
-
1. **Understand the Problem**
|
|
8
|
-
- Read the bug report or error description
|
|
9
|
-
- Reproduce the issue if possible
|
|
10
|
-
- Identify expected vs actual behavior
|
|
11
|
-
|
|
12
|
-
2. **Investigate**
|
|
13
|
-
- Search the codebase for relevant code paths
|
|
14
|
-
- Read error messages and stack traces carefully
|
|
15
|
-
- Use browser DevTools MCP if it's a UI issue
|
|
16
|
-
- Check recent changes that might have introduced the bug
|
|
17
|
-
- Add temporary logging if needed to trace execution
|
|
18
|
-
|
|
19
|
-
3. **Isolate the Root Cause**
|
|
20
|
-
- Narrow down to the specific file and function
|
|
21
|
-
- Understand why the current code produces the wrong behavior
|
|
22
|
-
- Verify your hypothesis by checking related code paths
|
|
23
|
-
|
|
24
|
-
4. **Write a Regression Test**
|
|
25
|
-
- Write a test that reproduces the bug (it should fail before the fix)
|
|
26
|
-
- Cover the specific edge case that caused the issue
|
|
27
|
-
|
|
28
|
-
5. **Fix the Bug**
|
|
29
|
-
- Make the minimal change needed to fix the issue
|
|
30
|
-
- Verify the regression test now passes
|
|
31
|
-
- Run the full test suite to check for regressions
|
|
32
|
-
- Clean up any temporary debugging code
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# Plan
|
|
2
|
-
|
|
3
|
-
Design an implementation approach before writing code.
|
|
4
|
-
|
|
5
|
-
## Instructions
|
|
6
|
-
|
|
7
|
-
1. **Gather Requirements**
|
|
8
|
-
- Read the task description carefully
|
|
9
|
-
- Identify acceptance criteria and constraints
|
|
10
|
-
- Ask clarifying questions if requirements are ambiguous
|
|
11
|
-
|
|
12
|
-
2. **Explore the Codebase**
|
|
13
|
-
- Find relevant files and understand existing patterns
|
|
14
|
-
- Check for similar implementations to follow as examples
|
|
15
|
-
- Identify files that will need changes
|
|
16
|
-
|
|
17
|
-
3. **Design the Approach**
|
|
18
|
-
- Break the task into concrete implementation steps
|
|
19
|
-
- Consider edge cases and error handling
|
|
20
|
-
- Note any dependencies or ordering constraints
|
|
21
|
-
- Estimate which files will be created or modified
|
|
22
|
-
|
|
23
|
-
4. **Present the Plan**
|
|
24
|
-
- Summarize the approach in a clear, numbered list
|
|
25
|
-
- Highlight any architectural decisions or trade-offs
|
|
26
|
-
- List files to be modified with a brief description of changes
|
|
27
|
-
- Call out any risks or open questions
|
|
28
|
-
|
|
29
|
-
5. **Wait for Approval**
|
|
30
|
-
- Do not start implementation until the developer approves
|
|
31
|
-
- Adjust the plan based on feedback
|
|
32
|
-
- Once approved, proceed with implementation
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# Pull Request
|
|
2
|
-
|
|
3
|
-
Create a well-structured pull request from the current changes.
|
|
4
|
-
|
|
5
|
-
## Instructions
|
|
6
|
-
|
|
7
|
-
1. **Analyze Changes**
|
|
8
|
-
- Run `git diff` to understand all changes
|
|
9
|
-
- Run `git log` to review commit history on this branch
|
|
10
|
-
- Identify the purpose and scope of the changes
|
|
11
|
-
|
|
12
|
-
2. **Draft PR Description**
|
|
13
|
-
- Write a clear title (under 70 characters)
|
|
14
|
-
- Summarize changes in 1-3 bullet points
|
|
15
|
-
- Reference related issues or tickets
|
|
16
|
-
- Note any breaking changes or migration steps
|
|
17
|
-
|
|
18
|
-
3. **Run Pre-flight Checks**
|
|
19
|
-
- Run the test suite and verify all tests pass
|
|
20
|
-
- Run the linter and fix any issues
|
|
21
|
-
- Run type checking and resolve any errors
|
|
22
|
-
- Verify the build succeeds
|
|
23
|
-
|
|
24
|
-
4. **Create the PR**
|
|
25
|
-
- Use `gh pr create` with the drafted title and description
|
|
26
|
-
- Add appropriate labels if the project uses them
|
|
27
|
-
- Request reviewers if specified
|
|
28
|
-
- Return the PR URL to the developer
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# Review
|
|
2
|
-
|
|
3
|
-
Review code changes against project standards.
|
|
4
|
-
|
|
5
|
-
## Instructions
|
|
6
|
-
|
|
7
|
-
1. **Understand Context**
|
|
8
|
-
- Read the PR description or task context
|
|
9
|
-
- Understand what the changes are trying to accomplish
|
|
10
|
-
- Check AGENTS.md for project-specific standards
|
|
11
|
-
|
|
12
|
-
2. **Review the Changes**
|
|
13
|
-
- Read all modified files carefully
|
|
14
|
-
- Check for correctness, readability, and maintainability
|
|
15
|
-
- Verify adherence to architecture standards (domain structure, typing, etc.)
|
|
16
|
-
- Look for potential bugs, edge cases, or security issues
|
|
17
|
-
- Check that tests cover the changes adequately
|
|
18
|
-
|
|
19
|
-
3. **Categorize Findings**
|
|
20
|
-
- **Must Fix**: Bugs, security issues, broken functionality, standards violations
|
|
21
|
-
- **Should Fix**: Code quality issues, missing tests, unclear naming
|
|
22
|
-
- **Suggestion**: Style preferences, optional improvements, alternative approaches
|
|
23
|
-
|
|
24
|
-
4. **Present the Review**
|
|
25
|
-
- Start with a summary of what the changes do
|
|
26
|
-
- List findings by category with file paths and line numbers
|
|
27
|
-
- Provide specific suggestions for how to fix each issue
|
|
28
|
-
- End with an overall assessment (approve, request changes, or needs discussion)
|