@meltstudio/meltctl 2.0.3 โ 2.2.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/README.md +19 -6
- package/dist/commands/project/clean.d.ts +5 -0
- package/dist/commands/project/clean.js +239 -0
- package/dist/commands/project/init.js +9 -10
- package/dist/index.js +7 -0
- package/package.json +1 -1
- package/templates/cursor-commands/melt-plan.md +22 -11
- package/templates/melt-memory/context.md +0 -23
- package/templates/melt-scripts/ps/common.ps1 +0 -48
- package/templates/melt-scripts/sh/common.sh +0 -44
package/README.md
CHANGED
|
@@ -70,6 +70,18 @@ Updates your project templates to the latest version. This command:
|
|
|
70
70
|
- Verifies .melt/ workspace migration status
|
|
71
71
|
- Handles version compatibility automatically
|
|
72
72
|
|
|
73
|
+
### Clean Project
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
meltctl project clean
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Safely removes all Melt-generated files from your project while preserving user-created content. This command:
|
|
80
|
+
- Removes the entire `.melt/` directory (all Melt-generated content)
|
|
81
|
+
- Selectively removes only Melt commands from `.cursor/commands/`
|
|
82
|
+
- Preserves user-created files in `.cursor/commands/`
|
|
83
|
+
- Provides interactive confirmation before deletion
|
|
84
|
+
|
|
73
85
|
## ๐ ๏ธ Requirements
|
|
74
86
|
|
|
75
87
|
- Node.js 22+ (works with Node.js 18+ but 22+ recommended)
|
|
@@ -120,12 +132,13 @@ This tool is part of the Melt Development Process. For issues or contributions:
|
|
|
120
132
|
The CLI uses a bundled template system organized in:
|
|
121
133
|
|
|
122
134
|
```
|
|
123
|
-
packages/cli/
|
|
124
|
-
โโโ
|
|
125
|
-
โโโ
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
135
|
+
packages/cli/
|
|
136
|
+
โโโ memory/ # Project context and standards
|
|
137
|
+
โโโ scripts/ # Utility scripts for sh and PowerShell
|
|
138
|
+
โ โโโ sh/ # Bash/zsh utility scripts
|
|
139
|
+
โ โโโ ps/ # PowerShell utility scripts
|
|
140
|
+
โโโ templates/
|
|
141
|
+
โโโ cursor-commands/ # All 8 Cursor AI command templates
|
|
129
142
|
```
|
|
130
143
|
|
|
131
144
|
Templates support dynamic content replacement (like timestamps) and are copied to your project during initialization.
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { intro, outro, confirm, spinner } from '@clack/prompts';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
// List of Melt-generated command files to remove
|
|
6
|
+
const MELT_COMMAND_FILES = [
|
|
7
|
+
'melt-plan.md',
|
|
8
|
+
'melt-test-plan.md',
|
|
9
|
+
'melt-docs.md',
|
|
10
|
+
'melt-implement.md',
|
|
11
|
+
'melt-pr.md',
|
|
12
|
+
'melt-review.md',
|
|
13
|
+
'melt-complete.md',
|
|
14
|
+
'melt-debug.md',
|
|
15
|
+
];
|
|
16
|
+
export async function cleanCommand(_options = {}) {
|
|
17
|
+
intro(chalk.blue('๐งน Melt Project - Clean'));
|
|
18
|
+
const currentDir = process.cwd();
|
|
19
|
+
const meltDir = path.join(currentDir, '.melt');
|
|
20
|
+
const cursorCommandsDir = path.join(currentDir, '.cursor', 'commands');
|
|
21
|
+
// Check if this is a Melt workspace
|
|
22
|
+
const hasMeltWorkspace = await fs.pathExists(meltDir);
|
|
23
|
+
const hasCursorCommands = await fs.pathExists(cursorCommandsDir);
|
|
24
|
+
if (!hasMeltWorkspace && !hasCursorCommands) {
|
|
25
|
+
console.log(chalk.yellow('โ ๏ธ No Melt workspace found in this directory.'));
|
|
26
|
+
console.log("This project doesn't appear to have Melt tools installed.");
|
|
27
|
+
outro(chalk.gray('Nothing to clean.'));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Analyze what will be cleaned
|
|
31
|
+
const analysisResult = await analyzeCleanupTarget(currentDir);
|
|
32
|
+
if (analysisResult.removedFiles.length === 0 && analysisResult.removedDirs.length === 0) {
|
|
33
|
+
console.log(chalk.yellow('โ ๏ธ No Melt files found to clean.'));
|
|
34
|
+
outro(chalk.gray('Nothing to clean.'));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// Show what will be cleaned
|
|
38
|
+
displayCleanupPlan(analysisResult);
|
|
39
|
+
// Get confirmation
|
|
40
|
+
const shouldProceed = await confirm({
|
|
41
|
+
message: 'Do you want to proceed with cleaning? This action cannot be undone.',
|
|
42
|
+
});
|
|
43
|
+
if (!shouldProceed) {
|
|
44
|
+
outro(chalk.gray('Clean operation cancelled.'));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Perform the cleanup
|
|
48
|
+
const s = spinner();
|
|
49
|
+
s.start('Cleaning Melt workspace...');
|
|
50
|
+
try {
|
|
51
|
+
const cleanResult = await performCleanup(currentDir);
|
|
52
|
+
s.stop('โ
Cleanup completed!');
|
|
53
|
+
displayCleanupResults(cleanResult);
|
|
54
|
+
outro(chalk.green('๐ Melt workspace cleaned successfully!'));
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
s.stop('โ Cleanup failed');
|
|
58
|
+
console.error(chalk.red('Error during cleanup:'), error);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function analyzeCleanupTarget(baseDir) {
|
|
63
|
+
const result = {
|
|
64
|
+
removedFiles: [],
|
|
65
|
+
removedDirs: [],
|
|
66
|
+
preservedFiles: [],
|
|
67
|
+
errors: [],
|
|
68
|
+
};
|
|
69
|
+
const meltDir = path.join(baseDir, '.melt');
|
|
70
|
+
const cursorCommandsDir = path.join(baseDir, '.cursor', 'commands');
|
|
71
|
+
try {
|
|
72
|
+
// Check .melt directory (remove entirely)
|
|
73
|
+
if (await fs.pathExists(meltDir)) {
|
|
74
|
+
result.removedDirs.push('.melt/');
|
|
75
|
+
}
|
|
76
|
+
// Check .cursor/commands directory (selective removal)
|
|
77
|
+
if (await fs.pathExists(cursorCommandsDir)) {
|
|
78
|
+
const commandFiles = await fs.readdir(cursorCommandsDir);
|
|
79
|
+
for (const file of commandFiles) {
|
|
80
|
+
const filePath = path.join(cursorCommandsDir, file);
|
|
81
|
+
const stat = await fs.stat(filePath);
|
|
82
|
+
if (stat.isFile()) {
|
|
83
|
+
if (MELT_COMMAND_FILES.includes(file)) {
|
|
84
|
+
result.removedFiles.push(path.join('.cursor/commands', file));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
result.preservedFiles.push(path.join('.cursor/commands', file));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Check if .cursor/commands will be empty after cleaning
|
|
92
|
+
const meltFiles = commandFiles.filter(file => MELT_COMMAND_FILES.includes(file));
|
|
93
|
+
const nonMeltFiles = commandFiles.filter(file => !MELT_COMMAND_FILES.includes(file));
|
|
94
|
+
if (meltFiles.length > 0 && nonMeltFiles.length === 0) {
|
|
95
|
+
result.removedDirs.push('.cursor/commands/');
|
|
96
|
+
// Also check if .cursor itself would be empty
|
|
97
|
+
const cursorDir = path.join(baseDir, '.cursor');
|
|
98
|
+
const cursorContents = await fs.readdir(cursorDir);
|
|
99
|
+
if (cursorContents.length === 1 && cursorContents[0] === 'commands') {
|
|
100
|
+
result.removedDirs.push('.cursor/');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
result.errors.push(`Failed to analyze cleanup target: ${error instanceof Error ? error.message : String(error)}`);
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
async function performCleanup(baseDir) {
|
|
111
|
+
const result = {
|
|
112
|
+
removedFiles: [],
|
|
113
|
+
removedDirs: [],
|
|
114
|
+
preservedFiles: [],
|
|
115
|
+
errors: [],
|
|
116
|
+
};
|
|
117
|
+
const meltDir = path.join(baseDir, '.melt');
|
|
118
|
+
const cursorCommandsDir = path.join(baseDir, '.cursor', 'commands');
|
|
119
|
+
try {
|
|
120
|
+
// Remove .melt directory entirely
|
|
121
|
+
if (await fs.pathExists(meltDir)) {
|
|
122
|
+
await fs.remove(meltDir);
|
|
123
|
+
result.removedDirs.push('.melt/');
|
|
124
|
+
}
|
|
125
|
+
// Remove Melt command files from .cursor/commands
|
|
126
|
+
if (await fs.pathExists(cursorCommandsDir)) {
|
|
127
|
+
const commandFiles = await fs.readdir(cursorCommandsDir);
|
|
128
|
+
let hasNonMeltFiles = false;
|
|
129
|
+
for (const file of commandFiles) {
|
|
130
|
+
const filePath = path.join(cursorCommandsDir, file);
|
|
131
|
+
const stat = await fs.stat(filePath);
|
|
132
|
+
if (stat.isFile()) {
|
|
133
|
+
if (MELT_COMMAND_FILES.includes(file)) {
|
|
134
|
+
try {
|
|
135
|
+
await fs.remove(filePath);
|
|
136
|
+
result.removedFiles.push(path.join('.cursor/commands', file));
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
result.errors.push(`Failed to remove ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
hasNonMeltFiles = true;
|
|
144
|
+
result.preservedFiles.push(path.join('.cursor/commands', file));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Remove empty directories if no user files remain
|
|
149
|
+
if (!hasNonMeltFiles) {
|
|
150
|
+
try {
|
|
151
|
+
await fs.remove(cursorCommandsDir);
|
|
152
|
+
result.removedDirs.push('.cursor/commands/');
|
|
153
|
+
// Check if .cursor directory is now empty
|
|
154
|
+
const cursorDir = path.join(baseDir, '.cursor');
|
|
155
|
+
if (await fs.pathExists(cursorDir)) {
|
|
156
|
+
const cursorContents = await fs.readdir(cursorDir);
|
|
157
|
+
if (cursorContents.length === 0) {
|
|
158
|
+
await fs.remove(cursorDir);
|
|
159
|
+
result.removedDirs.push('.cursor/');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
result.errors.push(`Failed to remove empty directories: ${error instanceof Error ? error.message : String(error)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
result.errors.push(`Cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
function displayCleanupPlan(result) {
|
|
175
|
+
console.log();
|
|
176
|
+
console.log(chalk.cyan('๐ Cleanup Plan:'));
|
|
177
|
+
console.log();
|
|
178
|
+
if (result.removedDirs.length > 0) {
|
|
179
|
+
console.log(chalk.yellow('Directories to be removed:'));
|
|
180
|
+
result.removedDirs.forEach(dir => {
|
|
181
|
+
console.log(` โข ${chalk.red(dir)}`);
|
|
182
|
+
});
|
|
183
|
+
console.log();
|
|
184
|
+
}
|
|
185
|
+
if (result.removedFiles.length > 0) {
|
|
186
|
+
console.log(chalk.yellow('Files to be removed:'));
|
|
187
|
+
result.removedFiles.forEach(file => {
|
|
188
|
+
console.log(` โข ${chalk.red(file)}`);
|
|
189
|
+
});
|
|
190
|
+
console.log();
|
|
191
|
+
}
|
|
192
|
+
if (result.preservedFiles.length > 0) {
|
|
193
|
+
console.log(chalk.green('Files to be preserved:'));
|
|
194
|
+
result.preservedFiles.forEach(file => {
|
|
195
|
+
console.log(` โข ${chalk.cyan(file)}`);
|
|
196
|
+
});
|
|
197
|
+
console.log();
|
|
198
|
+
}
|
|
199
|
+
if (result.errors.length > 0) {
|
|
200
|
+
console.log(chalk.red('Analysis errors:'));
|
|
201
|
+
result.errors.forEach(error => {
|
|
202
|
+
console.log(` โ ๏ธ ${error}`);
|
|
203
|
+
});
|
|
204
|
+
console.log();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function displayCleanupResults(result) {
|
|
208
|
+
console.log();
|
|
209
|
+
console.log(chalk.green('๐ฏ Cleanup Summary:'));
|
|
210
|
+
console.log();
|
|
211
|
+
if (result.removedDirs.length > 0) {
|
|
212
|
+
console.log(chalk.green(`โ
Removed ${result.removedDirs.length} directories:`));
|
|
213
|
+
result.removedDirs.forEach(dir => {
|
|
214
|
+
console.log(` โข ${dir}`);
|
|
215
|
+
});
|
|
216
|
+
console.log();
|
|
217
|
+
}
|
|
218
|
+
if (result.removedFiles.length > 0) {
|
|
219
|
+
console.log(chalk.green(`โ
Removed ${result.removedFiles.length} files:`));
|
|
220
|
+
result.removedFiles.forEach(file => {
|
|
221
|
+
console.log(` โข ${file}`);
|
|
222
|
+
});
|
|
223
|
+
console.log();
|
|
224
|
+
}
|
|
225
|
+
if (result.preservedFiles.length > 0) {
|
|
226
|
+
console.log(chalk.cyan(`๐ Preserved ${result.preservedFiles.length} user files:`));
|
|
227
|
+
result.preservedFiles.forEach(file => {
|
|
228
|
+
console.log(` โข ${file}`);
|
|
229
|
+
});
|
|
230
|
+
console.log();
|
|
231
|
+
}
|
|
232
|
+
if (result.errors.length > 0) {
|
|
233
|
+
console.log(chalk.red(`โ Errors encountered (${result.errors.length}):`));
|
|
234
|
+
result.errors.forEach(error => {
|
|
235
|
+
console.log(` โข ${error}`);
|
|
236
|
+
});
|
|
237
|
+
console.log();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -119,22 +119,21 @@ async function copyTemplates(baseDir, shell) {
|
|
|
119
119
|
if (existsSync(cursorCommandsTemplateDir)) {
|
|
120
120
|
fs.copySync(cursorCommandsTemplateDir, cursorCommandsDestDir);
|
|
121
121
|
}
|
|
122
|
-
// Create initial context file from
|
|
123
|
-
const
|
|
122
|
+
// Create initial context file from memory
|
|
123
|
+
const contextSourcePath = join(__dirname, '../../../memory', 'context.md');
|
|
124
124
|
const contextDestPath = join(baseDir, '.melt', 'memory', 'context.md');
|
|
125
|
-
if (existsSync(
|
|
126
|
-
let contextContent = readFileSync(
|
|
125
|
+
if (existsSync(contextSourcePath)) {
|
|
126
|
+
let contextContent = readFileSync(contextSourcePath, 'utf8');
|
|
127
127
|
// Replace timestamp placeholders
|
|
128
128
|
const timestamp = new Date().toISOString();
|
|
129
129
|
contextContent = contextContent.replace(/{{timestamp}}/g, timestamp);
|
|
130
130
|
fs.writeFileSync(contextDestPath, contextContent, 'utf8');
|
|
131
131
|
}
|
|
132
|
-
// Copy shell
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
fs.copySync(scriptTemplatePath, scriptDestPath);
|
|
132
|
+
// Copy shell scripts from CLI package
|
|
133
|
+
const scriptsSourceDir = join(__dirname, '../../../scripts', shell);
|
|
134
|
+
const scriptsDestDir = join(baseDir, '.melt', 'scripts', shell);
|
|
135
|
+
if (existsSync(scriptsSourceDir)) {
|
|
136
|
+
fs.copySync(scriptsSourceDir, scriptsDestDir);
|
|
138
137
|
}
|
|
139
138
|
}
|
|
140
139
|
function createVersionFile(meltDir) {
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { join, dirname } from 'path';
|
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { initCommand } from './commands/project/init.js';
|
|
7
7
|
import { updateCommand } from './commands/project/update.js';
|
|
8
|
+
import { cleanCommand } from './commands/project/clean.js';
|
|
8
9
|
// Read version from package.json
|
|
9
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
11
|
const __dirname = dirname(__filename);
|
|
@@ -34,4 +35,10 @@ projectCommand
|
|
|
34
35
|
.command('update')
|
|
35
36
|
.description('Update project configurations to latest version')
|
|
36
37
|
.action(updateCommand);
|
|
38
|
+
projectCommand
|
|
39
|
+
.command('clean')
|
|
40
|
+
.description('Remove all Melt-generated files from the project')
|
|
41
|
+
.action(() => {
|
|
42
|
+
return cleanCommand();
|
|
43
|
+
});
|
|
37
44
|
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Create implementation plan following Melt's domain-driven architecture standards
|
|
2
|
+
description: Create implementation plan with User Story and RFC validation following Melt's domain-driven architecture standards
|
|
3
|
+
scripts:
|
|
4
|
+
sh: .melt/scripts/sh/get-plan-context.sh --json
|
|
5
|
+
ps: .melt/scripts/ps/get-plan-context.ps1 -Json
|
|
3
6
|
---
|
|
4
7
|
|
|
5
8
|
Create a comprehensive implementation plan for the requested feature. Follow these steps:
|
|
6
9
|
|
|
7
|
-
1. **
|
|
8
|
-
-
|
|
10
|
+
1. **Collect Requirements**:
|
|
11
|
+
- Run `{SCRIPT}` from the project root and parse JSON for USER_STORY, RFC, ISSUE_ID, PLAN_FILE, and PROJECT_ROOT
|
|
12
|
+
- If script fails, prompt manually for User Story and RFC
|
|
13
|
+
- Validate that both User Story and RFC are complete and actionable
|
|
14
|
+
|
|
15
|
+
2. **Analyze Requirements**:
|
|
16
|
+
- Analyze the provided User Story for clear acceptance criteria and user value
|
|
17
|
+
- Review the RFC for technical decisions, architecture, and constraints
|
|
18
|
+
- Read `.melt/memory/context.md` for additional project context
|
|
9
19
|
- Identify the target domain(s) this feature belongs to
|
|
10
20
|
- Determine integration points with existing domains
|
|
11
21
|
|
|
12
|
-
|
|
22
|
+
3. **Architecture Planning**:
|
|
13
23
|
- Design domain structure following our patterns:
|
|
14
24
|
```
|
|
15
25
|
src/domains/[domain]/
|
|
@@ -23,27 +33,28 @@ Create a comprehensive implementation plan for the requested feature. Follow the
|
|
|
23
33
|
- Plan component hierarchy and state management
|
|
24
34
|
- Design data flow and API integration points
|
|
25
35
|
|
|
26
|
-
|
|
36
|
+
4. **Technical Decisions**:
|
|
27
37
|
- Choose appropriate React patterns (server vs client components)
|
|
28
38
|
- Plan Zod schemas for data validation
|
|
29
39
|
- Design TypeScript interfaces and types
|
|
30
40
|
- Plan testing strategy (unit, integration, e2e)
|
|
31
41
|
|
|
32
|
-
|
|
42
|
+
5. **Implementation Strategy**:
|
|
33
43
|
- Break down into atomic, testable units
|
|
34
44
|
- Identify dependencies and execution order
|
|
35
45
|
- Plan for error handling and edge cases
|
|
36
46
|
- Consider accessibility and performance requirements
|
|
37
47
|
|
|
38
|
-
|
|
48
|
+
6. **Create Detailed Plan**:
|
|
39
49
|
- List specific files to create/modify
|
|
40
50
|
- Define component interfaces and props
|
|
41
51
|
- Specify API endpoints and data contracts
|
|
42
52
|
- Plan test cases and coverage targets
|
|
43
53
|
|
|
44
|
-
|
|
45
|
-
- Save comprehensive plan to
|
|
46
|
-
-
|
|
47
|
-
-
|
|
54
|
+
7. **Save Planning Output**:
|
|
55
|
+
- Save comprehensive plan to the PLAN_FILE path provided by the script
|
|
56
|
+
- Include User Story and RFC as context sections in the plan
|
|
57
|
+
- Update `.melt/memory/context.md` with planning decisions and issue reference
|
|
58
|
+
- Create task breakdown for implementation with clear dependencies
|
|
48
59
|
|
|
49
60
|
Focus on maintainable, testable code that follows our domain-driven architecture principles.
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# Project Context
|
|
2
|
-
|
|
3
|
-
## Project Overview
|
|
4
|
-
This project uses Melt's development standards with domain-driven architecture.
|
|
5
|
-
|
|
6
|
-
## Architecture Principles
|
|
7
|
-
- **Domain-Driven Design**: Code organized by business domains
|
|
8
|
-
- **React 2025 Standards**: Functional components, hooks, TypeScript strict
|
|
9
|
-
- **Type Safety**: Zod schemas for runtime validation
|
|
10
|
-
- **Testing**: 80% coverage minimum with comprehensive test strategies
|
|
11
|
-
|
|
12
|
-
## Development Workflow
|
|
13
|
-
1. **Plan**: Create implementation plans in `.melt/outputs/plans/`
|
|
14
|
-
2. **Implement**: Follow domain architecture patterns
|
|
15
|
-
3. **Review**: Ensure quality and compliance
|
|
16
|
-
4. **Debug**: Systematic problem-solving approach
|
|
17
|
-
|
|
18
|
-
## Current Status
|
|
19
|
-
- Initialized: {{timestamp}}
|
|
20
|
-
- Last Updated: {{timestamp}}
|
|
21
|
-
|
|
22
|
-
## Notes
|
|
23
|
-
Update this file as the project evolves to maintain context for AI assistants and team members.
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env pwsh
|
|
2
|
-
# Common utilities for Melt development workflow
|
|
3
|
-
|
|
4
|
-
function Get-ProjectRoot {
|
|
5
|
-
try {
|
|
6
|
-
git rev-parse --show-toplevel
|
|
7
|
-
}
|
|
8
|
-
catch {
|
|
9
|
-
Get-Location
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function Get-Timestamp {
|
|
14
|
-
Get-Date -Format "yyyyMMdd-HHmmss"
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function Test-MeltWorkspace {
|
|
18
|
-
if (-not (Test-Path ".melt" -PathType Container)) {
|
|
19
|
-
Write-Error "ERROR: .melt workspace not found. Run 'meltctl project init' first."
|
|
20
|
-
exit 1
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function New-OutputFile {
|
|
25
|
-
param(
|
|
26
|
-
[string]$Category,
|
|
27
|
-
[string]$Name
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
$timestamp = Get-Timestamp
|
|
31
|
-
$filename = "$timestamp-$Name.md"
|
|
32
|
-
$filepath = ".melt/outputs/$Category/$filename"
|
|
33
|
-
|
|
34
|
-
New-Item -Path ".melt/outputs/$Category" -ItemType Directory -Force | Out-Null
|
|
35
|
-
return $filepath
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function Update-Context {
|
|
39
|
-
param([string]$Message)
|
|
40
|
-
|
|
41
|
-
$timestamp = Get-Date -Format "yyyy-MM-ddTHH:mm:ssK"
|
|
42
|
-
|
|
43
|
-
Add-Content -Path ".melt/memory/context.md" -Value "## Update: $timestamp"
|
|
44
|
-
Add-Content -Path ".melt/memory/context.md" -Value $Message
|
|
45
|
-
Add-Content -Path ".melt/memory/context.md" -Value ""
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
Write-Output "Melt development utilities loaded."
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Common utilities for Melt development workflow
|
|
3
|
-
|
|
4
|
-
# Get project root directory
|
|
5
|
-
get_project_root() {
|
|
6
|
-
git rev-parse --show-toplevel 2>/dev/null || pwd
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
# Get current timestamp for file naming
|
|
10
|
-
get_timestamp() {
|
|
11
|
-
date '+%Y%m%d-%H%M%S'
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
# Check if .melt directory exists
|
|
15
|
-
check_melt_workspace() {
|
|
16
|
-
if [[ ! -d ".melt" ]]; then
|
|
17
|
-
echo "ERROR: .melt workspace not found. Run 'meltctl project init' first."
|
|
18
|
-
exit 1
|
|
19
|
-
fi
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
# Create output file with timestamp
|
|
23
|
-
create_output_file() {
|
|
24
|
-
local category="$1"
|
|
25
|
-
local name="$2"
|
|
26
|
-
local timestamp=$(get_timestamp)
|
|
27
|
-
local filename="${timestamp}-${name}.md"
|
|
28
|
-
local filepath=".melt/outputs/${category}/${filename}"
|
|
29
|
-
|
|
30
|
-
mkdir -p ".melt/outputs/${category}"
|
|
31
|
-
echo "$filepath"
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
# Update context file with new information
|
|
35
|
-
update_context() {
|
|
36
|
-
local message="$1"
|
|
37
|
-
local timestamp=$(date -Iseconds)
|
|
38
|
-
|
|
39
|
-
echo "## Update: $timestamp" >> .melt/memory/context.md
|
|
40
|
-
echo "$message" >> .melt/memory/context.md
|
|
41
|
-
echo "" >> .melt/memory/context.md
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
echo "Melt development utilities loaded."
|