mddd-cli 1.0.3 → 1.0.4
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 +103 -103
- 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,222 @@ 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.
|
|
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 escape or remove function name parentheses. Use double quotes (e.g., A["main()"]) if the function name needs to be preserved, or simplify the node text (e.g., A[main]) to keep the diagram clean and avoid rendering errors.
|
|
85
|
+
2. ASCII Art or manual drawings are PROHIBITED.
|
|
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
90
|
`;
|
|
91
91
|
|
|
92
92
|
fs.writeFileSync('system_prompt.md', promptContent);
|
|
93
93
|
|
|
94
|
-
// 3.
|
|
94
|
+
// 3. Skill Definitions
|
|
95
95
|
const skills = {
|
|
96
|
-
'md-new': "
|
|
97
|
-
'md-edit': "
|
|
98
|
-
'md-audit': "
|
|
99
|
-
'md-impl': "
|
|
96
|
+
'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.",
|
|
97
|
+
'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 \`\`.",
|
|
98
|
+
'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: \`\`.",
|
|
99
|
+
'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
100
|
};
|
|
101
101
|
|
|
102
102
|
Object.keys(skills).forEach(skillName => {
|
|
103
|
-
// 1.
|
|
103
|
+
// 1. Create skill folder: .agents/skills/md-new/
|
|
104
104
|
const skillFolder = path.join(skillsDir, skillName);
|
|
105
105
|
if (!fs.existsSync(skillFolder)) {
|
|
106
106
|
fs.mkdirSync(skillFolder);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
// 2.
|
|
109
|
+
// 2. Create SKILL.md file inside: .agents/skills/md-new/SKILL.md
|
|
110
110
|
const skillFile = path.join(skillFolder, 'SKILL.md');
|
|
111
111
|
|
|
112
112
|
if (!fs.existsSync(skillFile)) {
|
|
113
|
-
//
|
|
113
|
+
// Adding an automatic title for better organization
|
|
114
114
|
const content = `# ${skillName.toUpperCase()}\n\n${skills[skillName]}`;
|
|
115
115
|
fs.writeFileSync(skillFile, content);
|
|
116
|
-
console.log(pc.green(`✅
|
|
116
|
+
console.log(pc.green(`✅ Encapsulated skill: ${skillFile}`));
|
|
117
117
|
}
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
-
console.log(pc.green('✅
|
|
120
|
+
console.log(pc.green('✅ Universal [system_prompt.md] file generated at the project root!'));
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
// ==========================================
|
|
124
|
-
//
|
|
124
|
+
// COMMAND: md new <targetPath>
|
|
125
125
|
// ==========================================
|
|
126
126
|
program
|
|
127
127
|
.command('new')
|
|
128
|
-
.description('
|
|
129
|
-
.argument('<targetPath>', '
|
|
130
|
-
.option('-m, --macro', '
|
|
131
|
-
.option('-p, --parent <parentFile>', '
|
|
128
|
+
.description('Creates a new co-located specification in Markdown, injects versioning, and links to the parent flow')
|
|
129
|
+
.argument('<targetPath>', 'Path to the feature directory (e.g., src/home/guest)')
|
|
130
|
+
.option('-m, --macro', 'Defines if the new file will be a module macro containing stateDiagram-v2')
|
|
131
|
+
.option('-p, --parent <parentFile>', 'Path to an existing spec (.spec.md) file to connect this new flow')
|
|
132
132
|
.action((targetPath, options) => {
|
|
133
|
-
//
|
|
133
|
+
// Ensures the base directory exists
|
|
134
134
|
if (!fs.existsSync(targetPath)) {
|
|
135
135
|
fs.mkdirSync(targetPath, { recursive: true });
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
138
|
+
// Correction: Extracts feature name for the file
|
|
139
|
+
// If targetPath ends in /routing, folderName will be 'routing'.
|
|
140
|
+
// The file will be 'routing.spec.md'.
|
|
141
141
|
const folderName = path.basename(targetPath);
|
|
142
142
|
const finalFile = path.join(targetPath, `${folderName}.spec.md`);
|
|
143
143
|
|
|
144
|
-
//
|
|
144
|
+
// Security: Verifies if the final path exists and is a directory
|
|
145
145
|
if (fs.existsSync(finalFile) && fs.lstatSync(finalFile).isDirectory()) {
|
|
146
|
-
console.log(pc.red(`❌
|
|
146
|
+
console.log(pc.red(`❌ Error: A directory with the name ${finalFile} already exists. Cannot create spec file.`));
|
|
147
147
|
process.exit(1);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
if (fs.existsSync(finalFile)) {
|
|
151
|
-
console.log(pc.yellow(`⚠️
|
|
151
|
+
console.log(pc.yellow(`⚠️ The specification already exists at: ${finalFile}`));
|
|
152
152
|
return;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
const isMacro = options.macro;
|
|
156
156
|
const version = 'v1.0.0';
|
|
157
157
|
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.
|
|
158
|
+
? `\n# Macro Module: ${folderName} | ${version}\n\n` +
|
|
159
|
+
`\`\`\`mermaid\n%% @spec-version ${version}\nstateDiagram-v2\n [*] --> Initial_${folderName}\n\`\`\`\n\n` +
|
|
160
|
+
`## 3. Audit History\n<details>\n<summary>Click to expand</summary>\n\n\n\n</details>\n`
|
|
161
|
+
: `\n# Specification: ${folderName} | ${version}\n\n` +
|
|
162
|
+
`## 1. Flow Contract (Mermaid)\n\`\`\`mermaid\n%% @spec-version ${version}\ngraph LR\n A([Start]) --> B[Process]\n\`\`\`\n\n` +
|
|
163
|
+
`## 2. Decision Matrix\n| Condition | Action | Next State |\n| :--- | :--- | :--- |\n| | | |\n\n` +
|
|
164
|
+
`## 3. Audit History\n<details>\n<summary>Click to expand</summary>\n\n\n\n</details>\n`;
|
|
165
165
|
|
|
166
166
|
fs.writeFileSync(finalFile, template);
|
|
167
|
-
console.log(pc.green(`✅
|
|
167
|
+
console.log(pc.green(`✅ New Markdown file created: ${finalFile}`));
|
|
168
168
|
|
|
169
|
-
//
|
|
169
|
+
// Linking Logic
|
|
170
170
|
let macroPath = options.parent || (!isMacro ? findClosestMacro(targetPath) : null);
|
|
171
171
|
|
|
172
172
|
if (macroPath) {
|
|
173
173
|
if (!fs.existsSync(macroPath)) {
|
|
174
|
-
console.log(pc.red(`❌
|
|
174
|
+
console.log(pc.red(`❌ Parent file not found: ${macroPath}`));
|
|
175
175
|
process.exit(1);
|
|
176
176
|
}
|
|
177
177
|
const relativePath = path.relative(path.dirname(macroPath), finalFile);
|
|
178
178
|
const cleanLinkPath = relativePath.replace(/\\/g, '/');
|
|
179
|
-
const injection = `\n\n%%
|
|
179
|
+
const injection = `\n\n%% Automatic connection for sub-flow\n- [Go to ${folderName} rules](file://./${cleanLinkPath})\n`;
|
|
180
180
|
|
|
181
181
|
fs.appendFileSync(macroPath, injection);
|
|
182
|
-
console.log(pc.blue(`🔗
|
|
182
|
+
console.log(pc.blue(`🔗 Successfully linked in parent file: ${macroPath}`));
|
|
183
183
|
}
|
|
184
184
|
});
|
|
185
185
|
|
|
186
186
|
// ==========================================
|
|
187
|
-
//
|
|
187
|
+
// COMMAND: md edit <specFilePath> <instruction>
|
|
188
188
|
// ==========================================
|
|
189
189
|
program
|
|
190
190
|
.command('edit')
|
|
191
|
-
.description('
|
|
192
|
-
.argument('<specFilePath>', '
|
|
193
|
-
.argument('<instruction...>', '
|
|
191
|
+
.description('Signals a pending change in an existing Mermaid specification file')
|
|
192
|
+
.argument('<specFilePath>', 'Path to the spec file (.spec.md)')
|
|
193
|
+
.argument('<instruction...>', 'The change instruction or flow adjustment')
|
|
194
194
|
.action((specFilePath, instruction) => {
|
|
195
195
|
if (!fs.existsSync(specFilePath)) {
|
|
196
|
-
console.log(pc.red(`❌
|
|
196
|
+
console.log(pc.red(`❌ Specification file not found: ${specFilePath}`));
|
|
197
197
|
process.exit(1);
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
const fullInstruction = instruction.join(' ');
|
|
201
|
-
console.log(pc.cyan(`📝
|
|
202
|
-
console.log(pc.yellow(`⚙️
|
|
203
|
-
console.log(pc.green(`\n🚀
|
|
201
|
+
console.log(pc.cyan(`📝 Requesting change to flow: "${specFilePath}"`));
|
|
202
|
+
console.log(pc.yellow(`⚙️ Evaluated instruction: ${fullInstruction}`));
|
|
203
|
+
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
204
|
});
|
|
205
205
|
|
|
206
206
|
// ==========================================
|
|
207
|
-
//
|
|
207
|
+
// COMMAND: md audit <codeFilePath>
|
|
208
208
|
// ==========================================
|
|
209
209
|
program
|
|
210
210
|
.command('audit')
|
|
211
|
-
.description('
|
|
212
|
-
.argument('<codeFilePath>', '
|
|
211
|
+
.description('Audits an existing code file to create a retroactive specification or suggest refactoring')
|
|
212
|
+
.argument('<codeFilePath>', 'Path to existing code file (e.g., src/services/user.go)')
|
|
213
213
|
.action((codeFilePath) => {
|
|
214
214
|
if (!fs.existsSync(codeFilePath)) {
|
|
215
|
-
console.log(pc.red(`❌
|
|
215
|
+
console.log(pc.red(`❌ Code file not found: ${codeFilePath}`));
|
|
216
216
|
process.exit(1);
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
const targetDir = path.dirname(codeFilePath);
|
|
220
220
|
const fileName = path.basename(codeFilePath);
|
|
221
221
|
|
|
222
|
-
console.log(pc.cyan(`🔍
|
|
223
|
-
console.log(pc.yellow(`⚡
|
|
222
|
+
console.log(pc.cyan(`🔍 Auditing code structure for coupling in: ${fileName}...`));
|
|
223
|
+
console.log(pc.yellow(`⚡ Requesting AI to validate complexity before generating MDDD specification.`));
|
|
224
224
|
|
|
225
225
|
if (!fs.existsSync(targetDir)) {
|
|
226
226
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
console.log(pc.green(`\n🚀
|
|
229
|
+
console.log(pc.green(`\n🚀 Ready! Use the /md-audit shortcut in the chat to receive the analysis or refactoring diagram.`));
|
|
230
230
|
});
|
|
231
231
|
|
|
232
232
|
// ==========================================
|
|
233
|
-
//
|
|
233
|
+
// COMMAND: md impl <specFilePath>
|
|
234
234
|
// ==========================================
|
|
235
235
|
program
|
|
236
236
|
.command('impl')
|
|
237
|
-
.description('
|
|
238
|
-
.argument('<specFilePath>', '
|
|
237
|
+
.description('Prepares the ecosystem to implement productive code and tests based on the spec file')
|
|
238
|
+
.argument('<specFilePath>', 'Path to the specification file (.spec.md)')
|
|
239
239
|
.action((specFilePath) => {
|
|
240
240
|
if (!fs.existsSync(specFilePath)) {
|
|
241
|
-
console.log(pc.red(`❌
|
|
241
|
+
console.log(pc.red(`❌ Specification file not found: ${specFilePath}`));
|
|
242
242
|
process.exit(1);
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
const fileName = path.basename(specFilePath);
|
|
246
|
-
console.log(pc.cyan(`🛠️
|
|
247
|
-
console.log(pc.yellow(`🎯
|
|
248
|
-
console.log(pc.green(`\n🚀
|
|
246
|
+
console.log(pc.cyan(`🛠️ Reading business blueprint from: ${fileName}...`));
|
|
247
|
+
console.log(pc.yellow(`🎯 Establishing the signed diagram as the Single Source of Truth.`));
|
|
248
|
+
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
249
|
});
|
|
250
250
|
|
|
251
251
|
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.4",
|
|
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": {
|