@meltstudio/meltctl 4.1.0 → 4.3.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.
@@ -1,5 +1,7 @@
1
1
  interface InitOptions {
2
2
  force?: boolean;
3
+ claude?: boolean;
4
+ cursor?: boolean;
3
5
  }
4
6
  export declare function initCommand(options: InitOptions): Promise<void>;
5
7
  export {};
@@ -1,4 +1,5 @@
1
1
  import chalk from 'chalk';
2
+ import { checkbox, confirm } from '@inquirer/prompts';
2
3
  import fs from 'fs-extra';
3
4
  import path from 'path';
4
5
  import { authenticatedFetch, isAuthenticated } from '../utils/auth.js';
@@ -37,12 +38,78 @@ async function fetchTemplates() {
37
38
  const data = (await response.json());
38
39
  return data.files;
39
40
  }
41
+ function detectExistingTools(cwd) {
42
+ return {
43
+ claude: fs.pathExistsSync(path.join(cwd, '.claude/settings.json')),
44
+ cursor: fs.pathExistsSync(path.join(cwd, '.cursor/commands')),
45
+ };
46
+ }
47
+ async function promptToolSelection(existingTools) {
48
+ const choices = [];
49
+ if (!existingTools?.claude) {
50
+ choices.push({ name: 'Claude Code', value: 'claude', checked: true });
51
+ }
52
+ if (!existingTools?.cursor) {
53
+ choices.push({ name: 'Cursor', value: 'cursor', checked: true });
54
+ }
55
+ choices.push({
56
+ name: 'Other — contact us in #dev on Slack to request support',
57
+ value: 'other',
58
+ });
59
+ const selected = await checkbox({
60
+ message: 'Which AI coding tools do you use?',
61
+ choices,
62
+ });
63
+ return {
64
+ claude: selected.includes('claude'),
65
+ cursor: selected.includes('cursor'),
66
+ other: selected.includes('other'),
67
+ };
68
+ }
40
69
  export async function initCommand(options) {
41
70
  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);
71
+ const alreadyInitialized = await fs.pathExists(path.join(cwd, 'AGENTS.md'));
72
+ let isReInit = false;
73
+ let tools;
74
+ if (alreadyInitialized && !options.force) {
75
+ // Re-init scenario: project already set up, developer wants to add another tool
76
+ const existing = detectExistingTools(cwd);
77
+ const existingNames = [existing.claude ? 'Claude Code' : '', existing.cursor ? 'Cursor' : '']
78
+ .filter(Boolean)
79
+ .join(' and ');
80
+ if (existing.claude && existing.cursor) {
81
+ console.log(chalk.yellow('Project already initialized with Claude Code and Cursor. Use --force to overwrite.'));
82
+ process.exit(1);
83
+ }
84
+ console.log(chalk.dim(`Project already initialized${existingNames ? ` with ${existingNames}` : ''}.`));
85
+ isReInit = true;
86
+ if (options.claude || options.cursor) {
87
+ // Non-interactive: use flags directly, skip confirm prompt
88
+ tools = { claude: !!options.claude, cursor: !!options.cursor, other: false };
89
+ }
90
+ else {
91
+ const addMore = await confirm({
92
+ message: 'Add configuration for another tool?',
93
+ default: true,
94
+ });
95
+ if (!addMore) {
96
+ process.exit(0);
97
+ }
98
+ tools = await promptToolSelection(existing);
99
+ }
100
+ }
101
+ else {
102
+ // Fresh init or --force
103
+ if (options.claude || options.cursor) {
104
+ tools = { claude: !!options.claude, cursor: !!options.cursor, other: false };
105
+ }
106
+ else {
107
+ tools = await promptToolSelection();
108
+ }
109
+ }
110
+ if (!tools.claude && !tools.cursor && !tools.other) {
111
+ console.log(chalk.yellow('No tools selected. Nothing to do.'));
112
+ process.exit(0);
46
113
  }
47
114
  // Require authentication
48
115
  if (!(await isAuthenticated())) {
@@ -67,69 +134,74 @@ export async function initCommand(options) {
67
134
  }
68
135
  console.log(chalk.bold('Initializing Melt development tools...'));
69
136
  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
137
+ const createdFiles = [];
82
138
  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');
139
+ // Shared files (skip on re-init)
140
+ if (!isReInit) {
141
+ const agentsMd = templates['agents-md.md'];
142
+ if (agentsMd) {
143
+ await fs.writeFile(path.join(cwd, 'AGENTS.md'), agentsMd, 'utf-8');
144
+ createdFiles.push('AGENTS.md');
90
145
  }
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');
146
+ const mcpConfig = templates['mcp-configs/base.json'];
147
+ if (mcpConfig) {
148
+ await fs.writeFile(path.join(cwd, '.mcp.json'), mcpConfig, 'utf-8');
149
+ createdFiles.push('.mcp.json');
104
150
  }
151
+ const envExample = templates['env-melt-example'];
152
+ if (envExample) {
153
+ await fs.writeFile(path.join(cwd, '.env.melt.example'), envExample, 'utf-8');
154
+ createdFiles.push('.env.melt.example');
155
+ }
156
+ await updateGitignore(cwd);
105
157
  }
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');
158
+ // Claude Code files
159
+ if (tools.claude) {
160
+ const claudeSettings = templates['claude-settings.json'];
161
+ if (claudeSettings) {
162
+ await fs.ensureDir(path.join(cwd, '.claude'));
163
+ await fs.writeFile(path.join(cwd, '.claude/settings.json'), claudeSettings, 'utf-8');
164
+ createdFiles.push('.claude/settings.json');
165
+ }
166
+ for (const name of workflows) {
167
+ const workflowContent = templates[`workflows/${name}.md`];
168
+ if (workflowContent) {
169
+ const skillDir = path.join(cwd, `.claude/skills/melt-${name}`);
170
+ await fs.ensureDir(skillDir);
171
+ const skillContent = SKILL_FRONTMATTER[name] + workflowContent;
172
+ await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillContent, 'utf-8');
173
+ }
174
+ }
175
+ createdFiles.push('.claude/skills/melt-{plan,review,pr,debug}/SKILL.md');
110
176
  }
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');
177
+ // Cursor files
178
+ if (tools.cursor) {
179
+ await fs.ensureDir(path.join(cwd, '.cursor/commands'));
180
+ for (const name of workflows) {
181
+ const workflowContent = templates[`workflows/${name}.md`];
182
+ if (workflowContent) {
183
+ await fs.writeFile(path.join(cwd, `.cursor/commands/melt-${name}.md`), workflowContent, 'utf-8');
184
+ }
185
+ }
186
+ createdFiles.push('.cursor/commands/melt-{plan,review,pr,debug}.md');
115
187
  }
116
- // Update .gitignore
117
- await updateGitignore(cwd);
118
188
  // Print summary
119
189
  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'));
190
+ for (const file of createdFiles) {
191
+ console.log(chalk.dim(` ${file}`));
192
+ }
132
193
  console.log();
194
+ if (tools.other) {
195
+ console.log(chalk.cyan('Want support for your tool? Let us know in #dev on Slack'));
196
+ console.log();
197
+ }
198
+ if (!isReInit) {
199
+ console.log(chalk.yellow('Next steps:'));
200
+ console.log(chalk.dim(' 1. Run your AI agent — it will automatically fill in project details in AGENTS.md'));
201
+ console.log(chalk.dim(' 2. Copy .env.melt.example to .env.local and fill in credentials'));
202
+ console.log(chalk.dim(' 3. Commit the generated files'));
203
+ console.log();
204
+ }
133
205
  console.log(chalk.green('Done!'));
134
206
  }
135
207
  async function updateGitignore(cwd) {
package/dist/index.js CHANGED
@@ -42,8 +42,10 @@ project
42
42
  .command('init')
43
43
  .description('scaffold Melt development tools into the current directory (AGENTS.md, .claude/, .cursor/, .mcp.json)')
44
44
  .option('--force', 'overwrite existing files if already initialized')
45
+ .option('--claude', 'generate Claude Code configuration')
46
+ .option('--cursor', 'generate Cursor configuration')
45
47
  .action(options => {
46
- return initCommand({ force: options.force });
48
+ return initCommand({ force: options.force, claude: options.claude, cursor: options.cursor });
47
49
  });
48
50
  program
49
51
  .command('version')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meltstudio/meltctl",
3
- "version": "4.1.0",
3
+ "version": "4.3.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",
@@ -48,6 +48,7 @@
48
48
  "license": "MIT",
49
49
  "dependencies": {
50
50
  "@commander-js/extra-typings": "^12.1.0",
51
+ "@inquirer/prompts": "^8.2.1",
51
52
  "chalk": "^5.4.1",
52
53
  "commander": "^12.1.0",
53
54
  "fs-extra": "^11.2.0",