mddd-cli 1.0.3 → 1.0.5
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/bin/cli.js +105 -104
- package/package.json +2 -2
package/bin/cli.js
CHANGED
|
@@ -7,20 +7,20 @@ import pc from 'picocolors';
|
|
|
7
7
|
|
|
8
8
|
const program = new Command();
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// Searches for the closest macro (*.spec.md) by recursively traversing the directory tree
|
|
11
11
|
function findClosestMacro(currentDir) {
|
|
12
12
|
let dir = currentDir;
|
|
13
13
|
while (dir !== path.parse(dir).root) {
|
|
14
14
|
try {
|
|
15
15
|
const files = fs.readdirSync(dir);
|
|
16
|
-
//
|
|
16
|
+
// Looks for any .spec.md file that is higher in the tree
|
|
17
17
|
const macroFile = files.find(f => f.endsWith('.spec.md') && f !== `${path.basename(currentDir)}.spec.md`);
|
|
18
18
|
|
|
19
19
|
if (macroFile) {
|
|
20
20
|
return path.join(dir, macroFile);
|
|
21
21
|
}
|
|
22
22
|
} catch (e) {
|
|
23
|
-
//
|
|
23
|
+
// Silences read permission errors in system folders
|
|
24
24
|
break;
|
|
25
25
|
}
|
|
26
26
|
dir = path.dirname(dir);
|
|
@@ -30,222 +30,223 @@ function findClosestMacro(currentDir) {
|
|
|
30
30
|
|
|
31
31
|
program
|
|
32
32
|
.name('md')
|
|
33
|
-
.description('
|
|
33
|
+
.description('Manager for co-located specifications for Mermaid Diagram Driven Development (MDDD)')
|
|
34
34
|
.version('3.0.0');
|
|
35
35
|
|
|
36
36
|
// ==========================================
|
|
37
|
-
//
|
|
37
|
+
// COMMAND: md init
|
|
38
38
|
// ==========================================
|
|
39
39
|
program
|
|
40
40
|
.command('init')
|
|
41
|
-
.description('
|
|
41
|
+
.description('Initializes the universal system prompt to guide any AI in the project under the MDDD methodology')
|
|
42
42
|
.action(() => {
|
|
43
43
|
const agentsDir = '.agents';
|
|
44
44
|
const skillsDir = path.join(agentsDir, 'skills');
|
|
45
45
|
|
|
46
|
-
// 1.
|
|
46
|
+
// 1. Creates folder structure
|
|
47
47
|
if (!fs.existsSync(agentsDir)) fs.mkdirSync(agentsDir);
|
|
48
48
|
if (!fs.existsSync(skillsDir)) fs.mkdirSync(skillsDir);
|
|
49
49
|
|
|
50
|
-
const promptContent = `#
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
## 1.
|
|
55
|
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
-
## 2.
|
|
60
|
-
|
|
61
|
-
1.
|
|
62
|
-
2.
|
|
63
|
-
3.
|
|
64
|
-
4.
|
|
65
|
-
|
|
66
|
-
## 3.
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
|
|
73
|
-
**
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
|
|
83
|
-
**
|
|
84
|
-
1.
|
|
85
|
-
2.
|
|
86
|
-
2.
|
|
87
|
-
3.
|
|
88
|
-
4.
|
|
89
|
-
5.
|
|
90
|
-
|
|
50
|
+
const promptContent = `# Mermaid Diagram Driven Development (MDDD) Protocol
|
|
51
|
+
|
|
52
|
+
You must strictly follow the modular feature specification architecture before changing, writing, or auditing productive code.
|
|
53
|
+
|
|
54
|
+
## 1. Tree Structure and Co-location
|
|
55
|
+
Visual specifications live universally in Markdown format (.md) exactly at the same level as the code they describe:
|
|
56
|
+
- Macro modules/domains have a \`[name].spec.md\` file containing the global diagram (stateDiagram-v2 syntax).
|
|
57
|
+
- Micro screens or sub-rule flows have a \`[name].spec.md\` file containing the interface flow (graph LR syntax) + Decision Tables.
|
|
58
|
+
|
|
59
|
+
## 2. Connection Rule Between Existing Flows
|
|
60
|
+
Whenever you create or change a functionality using an explicit parent file:
|
|
61
|
+
1. Open the indicated parent file BEFORE drawing the new flow.
|
|
62
|
+
2. Locate the exact node from which the business bifurcation should be born.
|
|
63
|
+
3. Modify the Mermaid code of the PARENT file to make the arrow point to the newly generated state.
|
|
64
|
+
4. In the CHILD file, start the graph using an entry node that inherits the parent's context.
|
|
65
|
+
|
|
66
|
+
## 3. Strict Diagram Versioning Rule
|
|
67
|
+
- Every file has a metadata header \`\`.
|
|
68
|
+
- Whenever you change a Mermaid diagram or a decision table using the \`/md-edit\` command, you MUST increment the file's semantic version in the header before saving:
|
|
69
|
+
- Change the Patch (\`v1.0.0\` -> \`v1.0.1\`) for syntax fixes or minor adjustments to node text.
|
|
70
|
+
- Change the Minor (\`v1.0.0\` -> \`v1.1.0\`) for new states, new transitions, or new columns in the decision matrix.
|
|
71
|
+
- Never remove the version tag. It is the guarantee that the code implementation is aligned with the correct design.
|
|
72
|
+
|
|
73
|
+
** SPECIFICATION WRITING GUIDELINE: **
|
|
74
|
+
Always use Mermaid to describe business flows, architecture, or state machines. Avoid as much as possible using running text or lists to describe complex logic.
|
|
75
|
+
Specifications (.spec.md) must be living documents focused on the Current Contract, not on past audits.
|
|
76
|
+
|
|
77
|
+
If the file is the Feature Contract: Focus only on:
|
|
78
|
+
- Mermaid Diagram (Real flow).
|
|
79
|
+
- Decision Matrix (Business rules).
|
|
80
|
+
- Signature of interfaces/services (API contract).
|
|
81
|
+
- Versioning: Keep SPEC_VERSION always at the top.
|
|
82
|
+
|
|
83
|
+
** RULES: **
|
|
84
|
+
1. When generating diagrams from code, always remove function name parentheses. Keep the diagram clean and avoid rendering errors.
|
|
85
|
+
2. ASCII Art or drawings are PROHIBITED. Use only Mermaid diagrams for visual representation.
|
|
86
|
+
2. Every diagram must be encapsulated in markdown code blocks with the language 'mermaid'.
|
|
87
|
+
3. For architecture flows or business logic, use exclusively 'graph TD' or 'graph LR'.
|
|
88
|
+
4. For (finite) state machines, use 'stateDiagram-v2'.
|
|
89
|
+
5. Name the nodes, use specific shapes ([...], ([...]), { ... }) to indicate intent (Action, Start/End, Decision).
|
|
90
|
+
6. ALWAYS WORK ON THE .SPEC.MD FILES. If they don't exist, create them. They are the single source of truth. Never make changes directly in the code without reflecting them in the diagrams.
|
|
91
|
+
`;
|
|
91
92
|
|
|
92
93
|
fs.writeFileSync('system_prompt.md', promptContent);
|
|
93
94
|
|
|
94
|
-
// 3.
|
|
95
|
+
// 3. Skill Definitions
|
|
95
96
|
const skills = {
|
|
96
|
-
'md-new': "
|
|
97
|
-
'md-edit': "
|
|
98
|
-
'md-audit': "
|
|
99
|
-
'md-impl': "
|
|
97
|
+
'md-new': "Drawing Mode. You must run the terminal command \`md new [path_to_new_feature]\` (and include \`-p [path]\` if there is a parent). Then, assemble the Mermaid and tables within the generated file and pause to await visual approval.",
|
|
98
|
+
'md-edit': "Editing Mode. Open the specified file, apply the change to the Mermaid or tables while keeping the syntax 100% valid, and increment the header \`\`.",
|
|
99
|
+
'md-audit': "Drastic Legacy Audit Mode. Analyze the existing code file from the perspective of visual readability (MDDD):\n1. If the code is modular, cohesive, and clean: Run the terminal command \`md new [file_directory]\`. Then, map the current flow in Mermaid, fill in the decision tables, and set the initial stable version as \`\`.\n2. If the code is chaotic, coupled, or complex: YOU ARE PROHIBITED from creating a stable diagram. Instead, point out the architectural problems, suggest a REFACTORING proposal separating responsibilities, and assemble the Mermaid of how the flow SHOULD BE post-refactoring. Save this spec file with a draft status: \`\`.",
|
|
100
|
+
'md-impl': "Implementation Mode. Read the \`.spec.md\` file from the path as your only Source of Truth and write the productive code and equivalent tests."
|
|
100
101
|
};
|
|
101
102
|
|
|
102
103
|
Object.keys(skills).forEach(skillName => {
|
|
103
|
-
// 1.
|
|
104
|
+
// 1. Create skill folder: .agents/skills/md-new/
|
|
104
105
|
const skillFolder = path.join(skillsDir, skillName);
|
|
105
106
|
if (!fs.existsSync(skillFolder)) {
|
|
106
107
|
fs.mkdirSync(skillFolder);
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
// 2.
|
|
110
|
+
// 2. Create SKILL.md file inside: .agents/skills/md-new/SKILL.md
|
|
110
111
|
const skillFile = path.join(skillFolder, 'SKILL.md');
|
|
111
112
|
|
|
112
113
|
if (!fs.existsSync(skillFile)) {
|
|
113
|
-
//
|
|
114
|
+
// Adding an automatic title for better organization
|
|
114
115
|
const content = `# ${skillName.toUpperCase()}\n\n${skills[skillName]}`;
|
|
115
116
|
fs.writeFileSync(skillFile, content);
|
|
116
|
-
console.log(pc.green(`✅
|
|
117
|
+
console.log(pc.green(`✅ Encapsulated skill: ${skillFile}`));
|
|
117
118
|
}
|
|
118
119
|
});
|
|
119
120
|
|
|
120
|
-
console.log(pc.green('✅
|
|
121
|
+
console.log(pc.green('✅ Universal [system_prompt.md] file generated at the project root!'));
|
|
121
122
|
});
|
|
122
123
|
|
|
123
124
|
// ==========================================
|
|
124
|
-
//
|
|
125
|
+
// COMMAND: md new <targetPath>
|
|
125
126
|
// ==========================================
|
|
126
127
|
program
|
|
127
128
|
.command('new')
|
|
128
|
-
.description('
|
|
129
|
-
.argument('<targetPath>', '
|
|
130
|
-
.option('-m, --macro', '
|
|
131
|
-
.option('-p, --parent <parentFile>', '
|
|
129
|
+
.description('Creates a new co-located specification in Markdown, injects versioning, and links to the parent flow')
|
|
130
|
+
.argument('<targetPath>', 'Path to the feature directory (e.g., src/home/guest)')
|
|
131
|
+
.option('-m, --macro', 'Defines if the new file will be a module macro containing stateDiagram-v2')
|
|
132
|
+
.option('-p, --parent <parentFile>', 'Path to an existing spec (.spec.md) file to connect this new flow')
|
|
132
133
|
.action((targetPath, options) => {
|
|
133
|
-
//
|
|
134
|
+
// Ensures the base directory exists
|
|
134
135
|
if (!fs.existsSync(targetPath)) {
|
|
135
136
|
fs.mkdirSync(targetPath, { recursive: true });
|
|
136
137
|
}
|
|
137
138
|
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
139
|
+
// Correction: Extracts feature name for the file
|
|
140
|
+
// If targetPath ends in /routing, folderName will be 'routing'.
|
|
141
|
+
// The file will be 'routing.spec.md'.
|
|
141
142
|
const folderName = path.basename(targetPath);
|
|
142
143
|
const finalFile = path.join(targetPath, `${folderName}.spec.md`);
|
|
143
144
|
|
|
144
|
-
//
|
|
145
|
+
// Security: Verifies if the final path exists and is a directory
|
|
145
146
|
if (fs.existsSync(finalFile) && fs.lstatSync(finalFile).isDirectory()) {
|
|
146
|
-
console.log(pc.red(`❌
|
|
147
|
+
console.log(pc.red(`❌ Error: A directory with the name ${finalFile} already exists. Cannot create spec file.`));
|
|
147
148
|
process.exit(1);
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
if (fs.existsSync(finalFile)) {
|
|
151
|
-
console.log(pc.yellow(`⚠️
|
|
152
|
+
console.log(pc.yellow(`⚠️ The specification already exists at: ${finalFile}`));
|
|
152
153
|
return;
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
const isMacro = options.macro;
|
|
156
157
|
const version = 'v1.0.0';
|
|
157
158
|
let template = isMacro
|
|
158
|
-
? `\n# Macro
|
|
159
|
-
`\`\`\`mermaid\n%% @spec-version ${version}\nstateDiagram-v2\n [*] -->
|
|
160
|
-
`## 3.
|
|
161
|
-
: `\n#
|
|
162
|
-
`## 1.
|
|
163
|
-
`## 2.
|
|
164
|
-
`## 3.
|
|
159
|
+
? `\n# Macro Module: ${folderName} | ${version}\n\n` +
|
|
160
|
+
`\`\`\`mermaid\n%% @spec-version ${version}\nstateDiagram-v2\n [*] --> Initial_${folderName}\n\`\`\`\n\n` +
|
|
161
|
+
`## 3. Audit History\n<details>\n<summary>Click to expand</summary>\n\n\n\n</details>\n`
|
|
162
|
+
: `\n# Specification: ${folderName} | ${version}\n\n` +
|
|
163
|
+
`## 1. Flow Contract (Mermaid)\n\`\`\`mermaid\n%% @spec-version ${version}\ngraph LR\n A([Start]) --> B[Process]\n\`\`\`\n\n` +
|
|
164
|
+
`## 2. Decision Matrix\n| Condition | Action | Next State |\n| :--- | :--- | :--- |\n| | | |\n\n` +
|
|
165
|
+
`## 3. Audit History\n<details>\n<summary>Click to expand</summary>\n\n\n\n</details>\n`;
|
|
165
166
|
|
|
166
167
|
fs.writeFileSync(finalFile, template);
|
|
167
|
-
console.log(pc.green(`✅
|
|
168
|
+
console.log(pc.green(`✅ New Markdown file created: ${finalFile}`));
|
|
168
169
|
|
|
169
|
-
//
|
|
170
|
+
// Linking Logic
|
|
170
171
|
let macroPath = options.parent || (!isMacro ? findClosestMacro(targetPath) : null);
|
|
171
172
|
|
|
172
173
|
if (macroPath) {
|
|
173
174
|
if (!fs.existsSync(macroPath)) {
|
|
174
|
-
console.log(pc.red(`❌
|
|
175
|
+
console.log(pc.red(`❌ Parent file not found: ${macroPath}`));
|
|
175
176
|
process.exit(1);
|
|
176
177
|
}
|
|
177
178
|
const relativePath = path.relative(path.dirname(macroPath), finalFile);
|
|
178
179
|
const cleanLinkPath = relativePath.replace(/\\/g, '/');
|
|
179
|
-
const injection = `\n\n%%
|
|
180
|
+
const injection = `\n\n%% Automatic connection for sub-flow\n- [Go to ${folderName} rules](file://./${cleanLinkPath})\n`;
|
|
180
181
|
|
|
181
182
|
fs.appendFileSync(macroPath, injection);
|
|
182
|
-
console.log(pc.blue(`🔗
|
|
183
|
+
console.log(pc.blue(`🔗 Successfully linked in parent file: ${macroPath}`));
|
|
183
184
|
}
|
|
184
185
|
});
|
|
185
186
|
|
|
186
187
|
// ==========================================
|
|
187
|
-
//
|
|
188
|
+
// COMMAND: md edit <specFilePath> <instruction>
|
|
188
189
|
// ==========================================
|
|
189
190
|
program
|
|
190
191
|
.command('edit')
|
|
191
|
-
.description('
|
|
192
|
-
.argument('<specFilePath>', '
|
|
193
|
-
.argument('<instruction...>', '
|
|
192
|
+
.description('Signals a pending change in an existing Mermaid specification file')
|
|
193
|
+
.argument('<specFilePath>', 'Path to the spec file (.spec.md)')
|
|
194
|
+
.argument('<instruction...>', 'The change instruction or flow adjustment')
|
|
194
195
|
.action((specFilePath, instruction) => {
|
|
195
196
|
if (!fs.existsSync(specFilePath)) {
|
|
196
|
-
console.log(pc.red(`❌
|
|
197
|
+
console.log(pc.red(`❌ Specification file not found: ${specFilePath}`));
|
|
197
198
|
process.exit(1);
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
const fullInstruction = instruction.join(' ');
|
|
201
|
-
console.log(pc.cyan(`📝
|
|
202
|
-
console.log(pc.yellow(`⚙️
|
|
203
|
-
console.log(pc.green(`\n🚀
|
|
202
|
+
console.log(pc.cyan(`📝 Requesting change to flow: "${specFilePath}"`));
|
|
203
|
+
console.log(pc.yellow(`⚙️ Evaluated instruction: ${fullInstruction}`));
|
|
204
|
+
console.log(pc.green(`\n🚀 Ready! Use the /md-edit shortcut in the chat for the AI to apply the changes and increment the version.`));
|
|
204
205
|
});
|
|
205
206
|
|
|
206
207
|
// ==========================================
|
|
207
|
-
//
|
|
208
|
+
// COMMAND: md audit <codeFilePath>
|
|
208
209
|
// ==========================================
|
|
209
210
|
program
|
|
210
211
|
.command('audit')
|
|
211
|
-
.description('
|
|
212
|
-
.argument('<codeFilePath>', '
|
|
212
|
+
.description('Audits an existing code file to create a retroactive specification or suggest refactoring')
|
|
213
|
+
.argument('<codeFilePath>', 'Path to existing code file (e.g., src/services/user.go)')
|
|
213
214
|
.action((codeFilePath) => {
|
|
214
215
|
if (!fs.existsSync(codeFilePath)) {
|
|
215
|
-
console.log(pc.red(`❌
|
|
216
|
+
console.log(pc.red(`❌ Code file not found: ${codeFilePath}`));
|
|
216
217
|
process.exit(1);
|
|
217
218
|
}
|
|
218
219
|
|
|
219
220
|
const targetDir = path.dirname(codeFilePath);
|
|
220
221
|
const fileName = path.basename(codeFilePath);
|
|
221
222
|
|
|
222
|
-
console.log(pc.cyan(`🔍
|
|
223
|
-
console.log(pc.yellow(`⚡
|
|
223
|
+
console.log(pc.cyan(`🔍 Auditing code structure for coupling in: ${fileName}...`));
|
|
224
|
+
console.log(pc.yellow(`⚡ Requesting AI to validate complexity before generating MDDD specification.`));
|
|
224
225
|
|
|
225
226
|
if (!fs.existsSync(targetDir)) {
|
|
226
227
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
227
228
|
}
|
|
228
229
|
|
|
229
|
-
console.log(pc.green(`\n🚀
|
|
230
|
+
console.log(pc.green(`\n🚀 Ready! Use the /md-audit shortcut in the chat to receive the analysis or refactoring diagram.`));
|
|
230
231
|
});
|
|
231
232
|
|
|
232
233
|
// ==========================================
|
|
233
|
-
//
|
|
234
|
+
// COMMAND: md impl <specFilePath>
|
|
234
235
|
// ==========================================
|
|
235
236
|
program
|
|
236
237
|
.command('impl')
|
|
237
|
-
.description('
|
|
238
|
-
.argument('<specFilePath>', '
|
|
238
|
+
.description('Prepares the ecosystem to implement productive code and tests based on the spec file')
|
|
239
|
+
.argument('<specFilePath>', 'Path to the specification file (.spec.md)')
|
|
239
240
|
.action((specFilePath) => {
|
|
240
241
|
if (!fs.existsSync(specFilePath)) {
|
|
241
|
-
console.log(pc.red(`❌
|
|
242
|
+
console.log(pc.red(`❌ Specification file not found: ${specFilePath}`));
|
|
242
243
|
process.exit(1);
|
|
243
244
|
}
|
|
244
245
|
|
|
245
246
|
const fileName = path.basename(specFilePath);
|
|
246
|
-
console.log(pc.cyan(`🛠️
|
|
247
|
-
console.log(pc.yellow(`🎯
|
|
248
|
-
console.log(pc.green(`\n🚀
|
|
247
|
+
console.log(pc.cyan(`🛠️ Reading business blueprint from: ${fileName}...`));
|
|
248
|
+
console.log(pc.yellow(`🎯 Establishing the signed diagram as the Single Source of Truth.`));
|
|
249
|
+
console.log(pc.green(`\n🚀 Ready! Use the /md-impl shortcut in the chat for the AI to start generating productive code and tests.`));
|
|
249
250
|
});
|
|
250
251
|
|
|
251
252
|
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mddd-cli",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "CLI
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "Official CLI for modular, co-located, and versioned Mermaid Diagram Driven Development (MDDD).",
|
|
5
5
|
"main": "bin/cli.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|