mddd-cli 2.1.2 → 2.1.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 CHANGED
@@ -32,7 +32,7 @@ const program = new Command();
32
32
  program
33
33
  .name('md')
34
34
  .description('Manager for co-located specifications for Mermaid Diagram Driven Development (MDDD)')
35
- .version('2.1.2');
35
+ .version('2.1.3');
36
36
 
37
37
  // ==========================================
38
38
  // COMMAND: md init
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "mddd-cli",
3
- "version": "2.1.2",
3
+ "version": "2.1.4",
4
4
  "description": "Official CLI for modular, co-located, and versioned Mermaid Diagram Driven Development (MDDD).",
5
- "main": "bin/cli.js",
6
5
  "type": "module",
6
+ "exports": "./bin/cli.js",
7
7
  "bin": {
8
- "md": "bin/cli.js"
8
+ "md": "./bin/cli.js"
9
9
  },
10
10
  "files": [
11
- "bin/"
11
+ "bin",
12
+ "src"
12
13
  ],
13
14
  "engines": {
14
15
  "node": ">=18.0.0"
@@ -40,4 +41,4 @@
40
41
  "commander": "^12.0.0",
41
42
  "picocolors": "^1.0.0"
42
43
  }
43
- }
44
+ }
package/readme.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  <p align="center">
10
10
  <a href="#english">🇺🇸 English</a> •
11
- <a href="#português">🇧🇷 Português</a>
11
+ <a href="#portuguese">🇧🇷 Português</a>
12
12
  </p>
13
13
 
14
14
  ---
@@ -307,6 +307,7 @@ If you encounter any issues, open a [GitHub Issue](https://github.com/JulioCRFil
307
307
  Distributed under the MIT license. See the [LICENSE](https://www.google.com/search?q=LICENSE) file for more information.
308
308
 
309
309
  ---
310
+ <a id="portuguese"></a>
310
311
 
311
312
  # 🇧🇷 Português
312
313
 
@@ -0,0 +1,22 @@
1
+ import pc from 'picocolors';
2
+ import { SpecGenerator } from '../services/SpecGenerator.js';
3
+ import { AuditService } from '../services/AuditService.js';
4
+
5
+ /**
6
+ * Executes the `md audit <codeFilePath>` command.
7
+ * @param {AuditService} auditService
8
+ * @param {SpecGenerator} specGenerator
9
+ * @param {string} codeFilePath
10
+ * @returns {Promise<void>}
11
+ */
12
+ export async function execute(auditService, specGenerator, codeFilePath) {
13
+ auditService.validateCodeFile(codeFilePath);
14
+
15
+ const { specFilePath: finalSpecPath } = await specGenerator.createIfMissing(codeFilePath);
16
+ const result = auditService.run(codeFilePath, finalSpecPath);
17
+
18
+ console.log(pc.blue(`📄 Existing specification: ${finalSpecPath}`));
19
+ console.log(pc.cyan(`🔍 Auditing code structure for coupling in: ${result.codeBasename}...`));
20
+ console.log(pc.yellow(`⚡ The AI will validate complexity and write the analysis to: ${finalSpecPath}`));
21
+ console.log(pc.green(`\n🚀 Ready! Use the /md-audit shortcut in chat for the AI to write the analysis and structural refactoring diagram into the co-located spec file.`));
22
+ }
@@ -0,0 +1,19 @@
1
+ import pc from 'picocolors';
2
+
3
+ /**
4
+ * Executes the `md edit <specFilePath>` command.
5
+ * @param {import('../services/SpecEditor.js').SpecEditor} specEditor
6
+ * @param {string} specFilePath
7
+ * @param {string[]} instructionParts
8
+ * @returns {void}
9
+ */
10
+ export function execute(specEditor, specFilePath, instructionParts) {
11
+ specEditor.validateSpec(specFilePath);
12
+
13
+ const fullInstruction = instructionParts.join(' ');
14
+ const prepared = specEditor.prepareInstruction(specFilePath, fullInstruction);
15
+
16
+ console.log(pc.cyan(`📝 Requesting alteration in flow: "${prepared.specFilePath}"`));
17
+ console.log(pc.yellow(`⚙️ Evaluated instruction: ${prepared.instruction}`));
18
+ console.log(pc.green(`\n🚀 Ready! Use the /md-edit shortcut in chat for the AI to apply changes to the diagram and increment the version.`));
19
+ }
@@ -0,0 +1,16 @@
1
+ import pc from 'picocolors';
2
+
3
+ /**
4
+ * Executes the `md impl <specFilePath>` command.
5
+ * @param {import('../services/ImplValidator.js').ImplValidator} implValidator
6
+ * @param {string} specFilePath
7
+ * @returns {void}
8
+ */
9
+ export function execute(implValidator, specFilePath) {
10
+ implValidator.validate(specFilePath);
11
+
12
+ const fileName = specFilePath.split('/').pop() || specFilePath.split('\\').pop();
13
+ console.log(pc.cyan(`🛠️ Reading business blueprint from: ${fileName}...`));
14
+ console.log(pc.yellow(`🎯 Establishing the signed diagram as the Single Source of Truth.`));
15
+ console.log(pc.green(`\n🚀 Ready! Use the /md-impl shortcut in chat for the AI to start generating productive code and tests.`));
16
+ }
@@ -0,0 +1,234 @@
1
+ import pc from 'picocolors';
2
+
3
+ /**
4
+ * PROMPT SYSTEM CONTENT: the full MDDD universal system prompt text.
5
+ * (Embedded here to maintain co-location; refactored from the monolithic cli.js)
6
+ */
7
+ export const SYSTEM_PROMPT_CONTENT = `# Mermaid Diagram Driven Development (MDDD) Protocol
8
+
9
+ You are an engineering agent operating strictly under MDDD. Your cognitive processing is guided by visual topologies and truth tables, completely eliminating text-based specification ambiguity.
10
+
11
+ \`\`\`mermaid
12
+ %% @spec-version v1.0.0
13
+ stateDiagram-v2
14
+ [*] --> ReadSpecification: User Trigger Fired
15
+ ReadSpecification --> CheckDecisionMatrix: Evaluate Primitive Factors
16
+ CheckDecisionMatrix --> HaltWithConflict: Constraint Violation / Feature Creep
17
+ CheckDecisionMatrix --> ExecuteAction: Strict Match Confirmed
18
+ ExecuteAction --> MutateState: Apply File/Code Changes
19
+ MutateState --> UpdateVersionHeader: Apply Semantic Version Rules
20
+ UpdateVersionHeader --> [*]
21
+ \`\`\`
22
+
23
+ ## 1. Co-location Architecture Tree
24
+
25
+ src/
26
+ └── [domain]/
27
+ ├── [domain_name].spec.md # 🌎 Macro Module Domain
28
+ └── [feature_name]/
29
+ ├── [feature_name].spec.md # 🔬 Micro Flow Contract + Decision Matrix
30
+ └── [feature_name].* # 💻 Target Production Code File (Any Extension)
31
+
32
+ ## 2. Parent Interaction Logic
33
+
34
+ \`\`\`mermaid
35
+ graph TD
36
+ A[Create/Change Sub-Feature] --> B[Open Indicated Parent File]
37
+ B --> C[Locate Bifurcation Node in Parent Mermaid]
38
+ C --> D[Modify Parent Graph: Point Arrow to New State]
39
+ D --> E[Child File: Inherit Parent Context in Entry Node]
40
+ \`\`\`
41
+
42
+ ## 3. Core Behavioral Framework Matrix
43
+
44
+ | User Context | Target Spec Header | Human Request Path | Diagram Change Impact | AI Core Rule / Mandate / Ironclad Clause |
45
+ | :---: | :---: | :---: | :---: | :--- |
46
+ | | - | **MISSING** | - | - | Never remove, omit, or bypass the version tag from files. |
47
+ | | Code Change Needed | **SIGNED** | Contradicts Matrix | - | 🛑 **HALT**: Refuse code generation. Demand \`md edit\` to align design first. |
48
+ | | Feature Writing | - | Continuous Text Block | - | 📊 **STRUCTURE**: Convert text into tables of primitive factors (yes/no/rigid values). |
49
+ | | Command Executed | \`SPEC_VERSION\` | - | Typo / Label Only | Increment Patch (\`X.Y.Z\` -> \`X.Y.Z+1\`) |
50
+ | | Command Executed | \`SPEC_VERSION\` | - | New State / Arrow / Matrix Column | Increment Minor (\`X.Y.Z\` -> \`X.Y+1.0\`) |
51
+ | | Command Executed | \`SPEC_VERSION\` | - | Structural Breaking / Flow Overhaul | Increment Major (\`X.Y.Z\` -> \`X+1.0.0\`) |
52
+
53
+ ## 4. Anti-Hallucination Guardrails
54
+ 1. **No Spec, No Code:** You are strictly forbidden from writing a single line of production code or unit tests if the corresponding \`.spec.md\` file does not exist or does not contain a populated Decision Matrix.
55
+ 2. **Implicit Logic Ban:** If a business condition, validation check, or outcome branch is not explicitly listed as a row or column in the Decision Matrix, it does not exist. Do not assume, extrapolate, or invent fallback behaviors.
56
+ 3. **Strict State Isolation:** When handling a micro feature, you cannot introduce global states or modify sibling domains unless instructed via explicit macro architectural mapping updates.
57
+ 4. **Idempotent Full-File Output Mandate:** You are completely forbidden from using code placeholders, truncating files, or emitting partial snippets (e.g., "// rest of class unchanged", "/* TODO */"). Every code generation action must output the entire, clean, compile-ready file from scratch, ensuring perfect context preservation.`;
58
+
59
+ /**
60
+ * Skills content map: skill name -> SKILL.md content.
61
+ */
62
+ export const SKILLS = {
63
+ 'md-new': `[ROLE: ARCHITECT] [STRICT CONTRACT]
64
+
65
+ \`\`\`mermaid
66
+ %% @spec-version v1.1.0
67
+ stateDiagram-v2
68
+ [*] --> TargetVerification
69
+ TargetVerification --> StopAndSwitchToEdit: .spec.md File Already Exists
70
+ TargetVerification --> EvaluateContext: File Does Not Exist
71
+
72
+ state EvaluateContext {
73
+ [*] --> CheckDirectoryDepth
74
+ CheckDirectoryDepth --> InferMacro: Target is Domain Root (e.g., src/domain)
75
+ CheckDirectoryDepth --> InferMicro: Target is Sub-Feature (e.g., src/domain/feature)
76
+ }
77
+
78
+ InferMacro --> ExecCliNew: Apply stateDiagram-v2 Template
79
+ InferMicro --> ExecCliNew: Apply graph LR + Matrix Template
80
+
81
+ ExecCliNew --> AwaitHumanReview: Run "md new [path]" & Populate Blueprint
82
+ AwaitHumanReview --> [*]: Pause Code & Test Generation
83
+ \`\`\`
84
+
85
+ ### Operational Execution Matrix
86
+
87
+ | File Exists? | Path Depth Type | Parent Indicated? | CLI Execution Syntax | Target Payload Blueprint | Next AI Action |
88
+ | :---: | :---: | :---: | :--- | :--- | :---: |
89
+ | ✅ YES | - | - | *None* (Aborted) | *None* | 🛑 **STOP** (Call md-edit instead) |
90
+ | ❌ NO | Domain Root | ❌ NO | \`md new [domain_path]\` | \`stateDiagram-v2\` Placeholder Domain Map | ⏳ **AWAIT_VISUAL_APPROVAL** |
91
+ | ❌ NO | Sub-Feature | ❌ NO | \`md new [feature_path]\` | \`graph LR\` + Auto-scanned parent link reference | ⏳ **AWAIT_VISUAL_APPROVAL** |
92
+ | ❌ NO | Sub-Feature | ✅ YES | \`md new [feature_path] -p[parent]\` | \`graph LR\` + Explicit link injected to designated Parent | ⏳ **AWAIT_VISUAL_APPROVAL** |
93
+
94
+ ### Automation & Inference Ironclad Rules
95
+ 1. **Deterministic Inference:** You must strictly follow the directory depth. If the target path is a top-level domain folder inside your source root, treat it as a Module Macro. If it is nested inside a domain, it is a Micro Feature. Never ask the user to declare this.
96
+ 2. **Implicit Parent Binding:** When creating a Sub-Feature without an explicit \`-p\` parameter, acknowledge that the CLI tool will automatically scan and mutate the nearest parent macro file via recursive climbing. You must read the updated parent immediately after execution to synchronize your internal context map.
97
+ 3. Agnostic Blueprint Initialization: When generating the initial blueprint files, you must scan the neighboring files in the target domain directory to identify the current programming language and framework conventions. Adapt your placeholder references to strictly pair with the localized file architecture.`,
98
+
99
+ 'md-edit': `[ROLE: ARCHITECT] [STRICT CONTRACT]
100
+
101
+ \`\`\`mermaid
102
+ %% @spec-version v1.1.0
103
+ graph LR
104
+ A[Read Target .spec.md] --> B[Parse Current SPEC_VERSION]
105
+ B --> C[Apply Mermaid/Matrix Adjustments]
106
+ C --> D{Evaluate Mutation Scope}
107
+
108
+ D -->|Typo / Label Fix| E[Increment Patch: Bump Z in X.Y.Z]
109
+ D -->|New Node / Flow Path / Factor| F[Increment Minor: Bump Y in X.Y.Z]
110
+ D -->|Breaking Overhaul / Restructure| G[Increment Major: Bump X in X.Y.Z]
111
+
112
+ E --> H[Validate Mermaid Syntax]
113
+ F --> H
114
+ G --> H
115
+
116
+ H -->|Syntax Valid| I[Save Contract & Halt]
117
+ H -->|Syntax Invalid| J[🛑 HALT: Abort & Ask Human]
118
+ \`\`\`
119
+
120
+ ### Evolution Versioning Matrix
121
+
122
+ | Structural Change Type | Adds Factor Column? | Adds Transition Node/Arrow? | Label / Typo Corrections Only? | Semantic Version Modification | Target AI State |
123
+ | :--- | :---: | :---: | :---: | :---: | :---: |
124
+ | Complete Business Overhaul | - | - | - | **MAJOR Mutation (X.Y.Z -> X+1.0.0)** | ⏳ **AWAIT_USER_VALIDATION** |
125
+ | New Context Conditional Branch | ✅ YES | - | - | **MINOR Mutation (X.Y.Z -> X.Y+1.0)** | ⏳ **AWAIT_USER_VALIDATION** |
126
+ | New UI Flow Step / Lifecycle State | ❌ NO | ✅ YES | - | **MINOR Mutation (X.Y.Z -> X.Y+1.0)** | ⏳ **AWAIT_USER_VALIDATION** |
127
+ | Visual Spacing / Text Refinement | ❌ NO | ❌ NO | ✅ YES | **PATCH Mutation (X.Y.Z -> X.Y.Z+1)** | ⏳ **AWAIT_USER_VALIDATION** |
128
+
129
+ ### Mutation Integrity Ironclad Rules
130
+ 1. **Incremental SemVer Locking:** You must read the existing \`SPEC_VERSION\` from the file header before modifying it. Never reset, guess, or overwrite the version to a lower state. Bumping Minor explicitly drops the patch version to zero (\`X.Y.Z\` -> \`X.Y+1.0\`). Bumping Major explicitly drops both minor and patch to zero (\`X.Y.Z\` -> \`X+1.0.0\`).
131
+ 2. **Strict Syntax Guard:** Before writing the modifications to disk, execute an internal mental compilation of the Mermaid syntax. If any arrow (\`-->\`), state connector, or label syntax breaks the official Mermaid spec, immediately halt execution and report the error to the user without modifying the file.
132
+ 3. **Audit History Log Requirement:** Every time you perform an edit, you must append a new row to the markdown table inside the \`<details><summary>Click to expand</summary>...</details>\` block at the bottom of the file, containing the current date, your agent identity, the new version number, and a concise summary of the changes made.
133
+ 4. **Node ID Immutability:** When adding new transitions or nodes to an existing graph, you are strictly forbidden from altering, renaming, or refactoring the identifiers (IDs) of existing states/nodes unless explicitly requested by the user.`,
134
+
135
+ 'md-audit': `[ROLE: SECURITY & QUALITY AUDITOR] [STRICT CONTRACT]
136
+
137
+ \`\`\`mermaid
138
+ %% @spec-version v1.1.0
139
+ stateDiagram-v2
140
+ [*] --> AnalyzeLegacyCode: Evaluate Coupling & Scope Leaks
141
+ AnalyzeLegacyCode --> FileSystemCheck
142
+
143
+ state FileSystemCheck {
144
+ [*] --> CheckCoLocation
145
+ CheckCoLocation --> CreateMissingSpec: Target Co-located .spec.md Missing
146
+ CheckCoLocation --> AppendToExisting: Target Co-located .spec.md Exists
147
+ }
148
+
149
+ CreateMissingSpec --> RenderTopology: Initialize New .spec.md
150
+ AppendToExisting --> InjectAuditBlock: Target Existing File Preservation Map
151
+
152
+ state RenderTopology {
153
+ [*] --> CodeIsClean: Map exact architecture as-is (v1.0.0)
154
+ [*] --> CodeIsChaotic: Draw BOTH current real logic AND ideal target refactored graph
155
+ }
156
+
157
+ RenderTopology --> WriteToAuditTag: Inject payloads inside <details> block
158
+ InjectAuditBlock --> WriteToAuditTag: Append to existing <details> block without overwriting business specs
159
+ WriteToAuditTag --> EnforceImmutability: Lock Production Code File
160
+ EnforceImmutability --> [*]
161
+ \`\`\`
162
+
163
+ ### Reverse Engineering & Auto-Repair Decision Matrix
164
+
165
+ | Source File State | Co-located .spec.md Exists? | Code Design Assessment | Target Output Destination | Code File Manipulation Allowed? | Initial Compiled Version |
166
+ | :--- | :---: | :---: | :--- | :---: | :---: |
167
+ | Legacy Code Active | ✅ YES | Clean / Modular | Append to existing \`<details><summary>Audit History</summary>\` | ❌ **FORBIDDEN (Immutability)** | Retain Current |
168
+ | Legacy Code Active | ✅ YES | Chaotic / Coupled | Append to existing \`<details><summary>Audit History</summary>\` | ❌ **FORBIDDEN (Immutability)** | Retain Current |
169
+ | Legacy Code Active | ❌ NO | Clean / Modular | Auto-generate Spec File + Map Current Logic | ❌ **FORBIDDEN (Immutability)** | \`v1.0.0\` |
170
+ | Legacy Code Active | ❌ NO | Chaotic / Coupled | Auto-generate Spec File + Map Current AND Proposed Logic | ❌ **FORBIDDEN (Immutability)** | \`v1.0.0\` |
171
+
172
+ ### Missing Spec Auto-Repair Blueprint Requirements
173
+ * **Enforce Section Injections:** Every auto-generated specification file must structurally enforce:
174
+ 1. \`SPEC_VERSION: v1.0.0\` metadata header at the very top.
175
+ 2. \`stateDiagram-v2\` or \`graph LR\` derived exactly from code logic behaviors.
176
+ 3. \`Decision Matrix\` tables filled if the code contains conditional execution branches.
177
+ 4. An isolated \`<details><summary>Audit History</summary>...</details>\` block at the bottom containing the specific code review analytics.
178
+
179
+ ### Quality Assurance & Immutability Ironclad Rules
180
+ 1. **Absolute Immutability Command:** Under no circumstances are you allowed to patch, alter, or modify the target production code file during the \`md-audit\` cycle. Your execution scope is strictly limited to observation and documentation within the Markdown specification file.
181
+ 2. **Preservation Guarantee:** When appending an audit report to an existing \`.spec.md\` file, you must read the file completely and guarantee that the business requirements, main diagrams, and current decision matrices are left untouched. You are only allowed to inject rows inside the \`<details>\` audit history block.
182
+ 3. **Chaotic Code Double-Mapping:** If you evaluate the legacy code as chaotic or highly coupled, you must not replace the current reality with your ideal version. You are required to draw the current graph (flawed as it is) to serve as a baseline, and then provide a separate, clearly labeled Mermaid graph showing the suggested refactored topology.`,
183
+
184
+ 'md-impl': `[ROLE: SOFTWARE ENGINEER] [STRICT CONTRACT]
185
+
186
+ \`\`\`mermaid
187
+ %% @spec-version v1.1.0
188
+ graph TD
189
+ A[Ingest Signed .spec.md] --> B[Parse Matrix Rows & Version Header]
190
+ B --> C{Verify Code/Chat Request}
191
+
192
+ C -->|Matches Decision Matrix Rows 100%| D[Check File Target State]
193
+ C -->|Human Asks to Skip/Add Extraneous Scope| E[Trigger Prompt Injection Defense]
194
+
195
+ D -->|New File| F[Generate Full Structural Code from Scratch]
196
+ D -->|Existing File| G[Idempotent Overwrite: Read & Output Full File]
197
+
198
+ F --> H[Generate Truth-Table Unit Tests]
199
+ G --> H
200
+
201
+ H --> I[Verify 100% Branch Coverage Alignment]
202
+ I --> [*]
203
+
204
+ E --> J[Refuse Coding & Demand Spec Refinement via md-edit]
205
+ J --> [*]
206
+ \`\`\`
207
+
208
+ ### Injection Defense & Execution Guard Matrix
209
+
210
+ | Spec Contract Signed? | Chat Prompt Code Alignment | Human Requests Bypassing Spec Matrix? | Core AI Action Authorized | Error Response Pattern |
211
+ | :---: | :---: | :---: | :--- | :--- |
212
+ | ❌ NO | - | - | ❌ **DENY GENERATION** | Demand invocation of \`md-new\` or \`md-audit\` |
213
+ | ✅ YES | ❌ Out-of-bounds | - | ❌ **DENY GENERATION** | "Please use the md-edit command to update the diagram..." |
214
+ | ✅ YES | - | ✅ YES (Feature Creep) | ❌ **DENY GENERATION** | "Please use the md-edit command to update the diagram..." |
215
+ | ✅ YES | ✅ 100% Rigid Match| ❌ NO | ✅ **ALLOW SOLID CODEGEN** | Complete compliance code + 100% matrix row unit tests |
216
+
217
+ ### Production Implementation & Codegen Ironclad Rules
218
+ 1. **The Matrix Test Alignment Mandate:** Your unit test suite must match the Decision Matrix row by row. For every single row present in the specification's truth table, you are strictly required to build at least one explicit, dedicated unit test case mapping those precise primitive factors to that exact outcome.
219
+ 2. **Anti-Placeholder Clause:** You are absolutely forbidden from generating incomplete code structures, omitting code sections, or using placeholders like \`// TODO\`, \`// implementation goes here\`, or \`// rest of the class remains unchanged\`. You must always output the complete, compile-ready, and production-grade file layout.
220
+ 3. **Strict SOLID Compliance:** Every piece of logic generated under this cycle must follow strict Clean Architecture principles and SOLID patterns. If the specification implies a new conditional branch, you must implement it using polymorphism or structured strategies rather than compounding nested \`if-else\` or pattern-matching anti-patterns unless explicitly dictated by the diagram topology.`,
221
+ };
222
+
223
+ /**
224
+ * Executes the `md init` command.
225
+ * @param {import('../services/InitService.js').InitService} initService
226
+ * @returns {Promise<void>}
227
+ */
228
+ export async function execute(initService) {
229
+ await initService.createSystemPrompt(SYSTEM_PROMPT_CONTENT);
230
+ await initService.createSkills(SKILLS, (msg) => console.log(msg));
231
+
232
+ console.log(pc.green('\n🚀 Universal [system_prompt.md] and SKILLS generated successfully in the project root!'));
233
+ console.log(pc.green('Run the "md init" command whenever you update the MDDD-CLI NPM package.'));
234
+ }
@@ -0,0 +1,56 @@
1
+ import path from 'node:path';
2
+ import pc from 'picocolors';
3
+
4
+ /**
5
+ * Executes the `md new <targetPath>` command.
6
+ * @param {import('../services/SpecGenerator.js').SpecGenerator} specGenerator
7
+ * @param {import('../services/ParentLinker.js').ParentLinker} parentLinker
8
+ * @param {import('../services/FileSystemService.js').FileSystemService} fs
9
+ * @param {string} targetPath
10
+ * @param {{ macro?: boolean, parent?: string }} options
11
+ * @returns {Promise<void>}
12
+ */
13
+ export async function execute(specGenerator, parentLinker, fs, targetPath, options) {
14
+ const normalizedPath = path.normalize(targetPath).replace(/[\\/]+$/, '');
15
+
16
+ fs.ensureDir(normalizedPath);
17
+
18
+ const folderName = path.basename(normalizedPath);
19
+ const finalFile = path.join(normalizedPath, `${folderName}.spec.md`);
20
+
21
+ if (fs.existsSync(finalFile)) {
22
+ // Check if it's a directory (edge case)
23
+ try {
24
+ const stats = await fs.getRaw().stat?.(finalFile);
25
+ if (stats?.isDirectory()) {
26
+ console.log(pc.red(`❌ Error: A directory named ${finalFile} already exists. Cannot create specification file.`));
27
+ process.exit(1);
28
+ }
29
+ } catch {
30
+ // stat not available in mock, fall through to normal check
31
+ }
32
+ }
33
+
34
+ if (fs.existsSync(finalFile)) {
35
+ console.log(pc.yellow(`⚠️ Specification already exists at: ${finalFile}. Operation aborted to avoid link duplication in the parent file.`));
36
+ process.exit(0);
37
+ }
38
+
39
+ const isMacro = options.macro || false;
40
+ const version = 'v1.0.0';
41
+
42
+ const { filePath } = await specGenerator.create(normalizedPath, isMacro, version);
43
+ console.log(pc.green(`✅ New specification file created: ${filePath}`));
44
+
45
+ let macroPath = options.parent || (!isMacro ? parentLinker.findClosestMacro(normalizedPath) : null);
46
+
47
+ if (macroPath) {
48
+ if (!fs.existsSync(macroPath)) {
49
+ console.log(pc.red(`❌ Specified parent file not found: ${macroPath}`));
50
+ process.exit(1);
51
+ }
52
+
53
+ await parentLinker.linkToParent(macroPath, filePath, folderName);
54
+ console.log(pc.blue(`🔗 Successfully linked into parent flow: ${macroPath}`));
55
+ }
56
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Handles the `md audit` command business logic.
3
+ */
4
+ export class AuditService {
5
+ /** @type {import('./FileSystemService.js').FileSystemService} */
6
+ #fs;
7
+
8
+ /**
9
+ * @param {import('./FileSystemService.js').FileSystemService} fsService
10
+ */
11
+ constructor(fsService) {
12
+ this.#fs = fsService;
13
+ }
14
+
15
+ /**
16
+ * Validates that a code file exists.
17
+ * @param {string} codeFilePath
18
+ * @returns {boolean}
19
+ * @throws {Error} if not found
20
+ */
21
+ validateCodeFile(codeFilePath) {
22
+ if (!this.#fs.existsSync(codeFilePath)) {
23
+ throw new Error(`Code file not found: ${codeFilePath}`);
24
+ }
25
+ return true;
26
+ }
27
+
28
+ /**
29
+ * Runs audit placeholders (actual AI analysis happens in chat via /md-audit skill).
30
+ * @param {string} codeFilePath
31
+ * @param {string} specFilePath
32
+ * @returns {{ codeBasename: string, specFilePath: string }}
33
+ */
34
+ run(codeFilePath, specFilePath) {
35
+ const basename = codeFilePath.split('/').pop() || codeFilePath.split('\\').pop();
36
+ return {
37
+ codeBasename: basename,
38
+ specFilePath,
39
+ };
40
+ }
41
+ }
@@ -0,0 +1,100 @@
1
+ // @ts-check
2
+ import fs from 'node:fs/promises';
3
+ import { existsSync, mkdirSync, readdirSync } from 'node:fs';
4
+
5
+ /**
6
+ * @typedef {Object} FileSystemOperations
7
+ * @property {(path: string) => Promise<string>} readFile
8
+ * @property {(path: string, content: string) => Promise<void>} writeFile
9
+ * @property {(path: string, content: string) => Promise<void>} appendFile
10
+ * @property {(path: string) => boolean} existsSync
11
+ * @property {(path: string) => void} mkdirSyncRecursive
12
+ * @property {(path: string) => string[]} readdirSync
13
+ */
14
+
15
+ /**
16
+ * Shared file system service with dependency injection support for testability.
17
+ */
18
+ export class FileSystemService {
19
+ /** @type {FileSystemOperations} */
20
+ #fs;
21
+
22
+ /**
23
+ * @param {Partial<FileSystemOperations>} [fsMock] - Optional mock for testing
24
+ */
25
+ constructor(fsMock) {
26
+ this.#fs = {
27
+ readFile: fsMock?.readFile || fs.readFile.bind(fs),
28
+ writeFile: fsMock?.writeFile || fs.writeFile.bind(fs),
29
+ appendFile: fsMock?.appendFile || fs.appendFile.bind(fs),
30
+ existsSync: fsMock?.existsSync || existsSync,
31
+ mkdirSyncRecursive: fsMock?.mkdirSyncRecursive || ((p) => mkdirSync(p, { recursive: true })),
32
+ readdirSync: fsMock?.readdirSync || readdirSync,
33
+ };
34
+ }
35
+
36
+ /**
37
+ * Checks if a path exists synchronously.
38
+ * @param {string} path
39
+ * @returns {boolean}
40
+ */
41
+ existsSync(path) {
42
+ return this.#fs.existsSync(path);
43
+ }
44
+
45
+ /**
46
+ * Creates a directory recursively.
47
+ * @param {string} path
48
+ */
49
+ mkdirSyncRecursive(path) {
50
+ this.#fs.mkdirSyncRecursive(path);
51
+ }
52
+
53
+ /**
54
+ * Creates a directory if it doesn't exist.
55
+ * @param {string} path
56
+ */
57
+ ensureDir(path) {
58
+ if (!this.#fs.existsSync(path)) {
59
+ this.#fs.mkdirSyncRecursive(path);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Reads a file as UTF-8 string.
65
+ * @param {string} path
66
+ * @returns {Promise<string>}
67
+ */
68
+ async readFile(path) {
69
+ return this.#fs.readFile(path);
70
+ }
71
+
72
+ /**
73
+ * Writes a string to a file.
74
+ * @param {string} path
75
+ * @param {string} content
76
+ * @returns {Promise<void>}
77
+ */
78
+ async writeFile(path, content) {
79
+ return this.#fs.writeFile(path, content);
80
+ }
81
+
82
+ /**
83
+ * Appends content to a file.
84
+ * @param {string} path
85
+ * @param {string} content
86
+ * @returns {Promise<void>}
87
+ */
88
+ async appendFile(path, content) {
89
+ return this.#fs.appendFile(path, content);
90
+ }
91
+
92
+ /**
93
+ * Synchronous readdir for directory crawling.
94
+ * @param {string} dir
95
+ * @returns {string[]}
96
+ */
97
+ readdirSync(dir) {
98
+ return this.#fs.readdirSync(dir);
99
+ }
100
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Validates prerequisites for the `md impl` command.
3
+ */
4
+ export class ImplValidator {
5
+ /** @type {import('./FileSystemService.js').FileSystemService} */
6
+ #fs;
7
+
8
+ /**
9
+ * @param {import('./FileSystemService.js').FileSystemService} fsService
10
+ */
11
+ constructor(fsService) {
12
+ this.#fs = fsService;
13
+ }
14
+
15
+ /**
16
+ * Validates that a spec file exists before implementation.
17
+ * @param {string} specFilePath
18
+ * @returns {boolean}
19
+ * @throws {Error} if not found
20
+ */
21
+ validate(specFilePath) {
22
+ if (!this.#fs.existsSync(specFilePath)) {
23
+ throw new Error(`Specification file not found: ${specFilePath}`);
24
+ }
25
+ return true;
26
+ }
27
+ }
@@ -0,0 +1,52 @@
1
+ import path from 'node:path';
2
+
3
+ /**
4
+ * Handles the `md init` business logic: system prompt and skills creation.
5
+ */
6
+ export class InitService {
7
+ /** @type {import('./FileSystemService.js').FileSystemService} */
8
+ #fs;
9
+
10
+ /**
11
+ * @param {import('./FileSystemService.js').FileSystemService} fsService
12
+ */
13
+ constructor(fsService) {
14
+ this.#fs = fsService;
15
+ }
16
+
17
+ /**
18
+ * Creates the universal system prompt file.
19
+ * @param {string} promptContent - The full MDDD system prompt content
20
+ * @returns {Promise<void>}
21
+ */
22
+ async createSystemPrompt(promptContent) {
23
+ await this.#fs.writeFile('system_prompt.md', promptContent);
24
+ }
25
+
26
+ /**
27
+ * Creates all skill folders and SKILL.md files.
28
+ * @param {Record<string, string>} skills - Map of skill name to skill content
29
+ * @returns {Promise<{console: (message: string) => void}>} Array of file paths created
30
+ */
31
+ async createSkills(skills, logger) {
32
+ const agentsDir = '.agents';
33
+ const skillsDir = path.join(agentsDir, 'skills');
34
+
35
+ this.#fs.ensureDir(agentsDir);
36
+ this.#fs.ensureDir(skillsDir);
37
+
38
+ const created = [];
39
+
40
+ for (const [skillName, content] of Object.entries(skills)) {
41
+ const skillFolder = path.join(skillsDir, skillName);
42
+ this.#fs.ensureDir(skillFolder);
43
+
44
+ const skillFile = path.join(skillFolder, 'SKILL.md');
45
+ await this.#fs.writeFile(skillFile, content);
46
+ created.push(skillFile);
47
+ logger(`✅ Skill successfully encapsulated: ${skillFile}`);
48
+ }
49
+
50
+ return created;
51
+ }
52
+ }
@@ -0,0 +1,70 @@
1
+ import path from 'node:path';
2
+
3
+ /**
4
+ * Crawls directories upward to find the nearest parent macro .spec.md file.
5
+ */
6
+ export class ParentLinker {
7
+ /** @type {import('./FileSystemService.js').FileSystemService} */
8
+ #fs;
9
+
10
+ /**
11
+ * @param {import('./FileSystemService.js').FileSystemService} fsService
12
+ */
13
+ constructor(fsService) {
14
+ this.#fs = fsService;
15
+ }
16
+
17
+ /**
18
+ * Searches for the closest macro (*.spec.md) by recursively traversing the directory tree upward.
19
+ * Skips the spec file that matches the current folder name to avoid self-linking.
20
+ * @param {string} currentDir - Absolute path of the feature directory
21
+ * @returns {string|null} Path to the parent .spec.md, or null if not found
22
+ */
23
+ findClosestMacro(currentDir) {
24
+ let dir = path.resolve(currentDir);
25
+ const root = path.parse(dir).root;
26
+
27
+ while (dir !== root) {
28
+ try {
29
+ const files = this.#fs.readdirSync(dir);
30
+ const macroFile = files.find(
31
+ (f) => f.endsWith('.spec.md') && f !== `${path.basename(currentDir)}.spec.md`
32
+ );
33
+
34
+ if (macroFile) {
35
+ return path.join(dir, macroFile);
36
+ }
37
+ } catch (e) {
38
+ // Permission errors: stop climbing and return null
39
+ if (e.code === 'EACCES' || e.code === 'EPERM') {
40
+ break;
41
+ }
42
+ throw e;
43
+ }
44
+
45
+ const parent = path.dirname(dir);
46
+ if (parent === dir) break;
47
+ dir = parent;
48
+ }
49
+ return null;
50
+ }
51
+
52
+ /**
53
+ * Appends a markdown link to the child spec into the parent spec file.
54
+ * @param {string} parentSpecPath - Path to the parent .spec.md
55
+ * @param {string} childSpecPath - Path to the child .spec.md
56
+ * @param {string} folderName - Name of the child feature folder
57
+ * @returns {Promise<void>}\n */
58
+ async linkToParent(parentSpecPath, childSpecPath, folderName) {
59
+ const relativePath = path
60
+ .relative(path.dirname(parentSpecPath), childSpecPath)
61
+ .replace(/\\/g, '/'); // Garante compatibilidade de paths no estilo POSIX para o Markdown
62
+
63
+ const parentContent = await this.#fs.readFile(parentSpecPath);
64
+
65
+ // Injeta o link logo após o fim do bloco do Mermaid ou no topo do arquivo estruturado
66
+ const linkAddition = `\n\n- [Micro Feature: ${folderName}](${relativePath})`;
67
+
68
+ await this.#fs.writeFile(parentSpecPath, parentContent + linkAddition);
69
+ }
70
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Handles the `md edit` command business logic.
3
+ */
4
+ export class SpecEditor {
5
+ /** @type {import('./FileSystemService.js').FileSystemService} */
6
+ #fs;
7
+
8
+ /**
9
+ * @param {import('./FileSystemService.js').FileSystemService} fsService
10
+ */
11
+ constructor(fsService) {
12
+ this.#fs = fsService;
13
+ }
14
+
15
+ /**
16
+ * Validates that a spec file exists.
17
+ * @param {string} specFilePath
18
+ * @returns {boolean}
19
+ * @throws {Error} if not found
20
+ */
21
+ validateSpec(specFilePath) {
22
+ if (!this.#fs.existsSync(specFilePath)) {
23
+ throw new Error(`Specification file not found: ${specFilePath}`);
24
+ }
25
+ return true;
26
+ }
27
+
28
+ /**
29
+ * Prepares the edit instruction message (placeholder — actual logic applied by AI agent).
30
+ * @param {string} specFilePath
31
+ * @param {string} instruction
32
+ * @returns {{ specFilePath: string, instruction: string }}
33
+ */
34
+ prepareInstruction(specFilePath, instruction) {
35
+ return { specFilePath, instruction };
36
+ }
37
+ }
@@ -0,0 +1,58 @@
1
+ import path from 'node:path';
2
+ import { TemplateFactory } from './TemplateFactory.js';
3
+
4
+ /**
5
+ * Handles .spec.md file generation for `md new` and `md audit` commands.
6
+ */
7
+ export class SpecGenerator {
8
+ /** @type {import('./FileSystemService.js').FileSystemService} */
9
+ #fs;
10
+
11
+ /**
12
+ * @param {import('./FileSystemService.js').FileSystemService} fsService
13
+ */
14
+ constructor(fsService) {
15
+ this.#fs = fsService;
16
+ }
17
+
18
+ /**
19
+ * Creates a new .spec.md file for a feature (macro or micro).
20
+ * @param {string} targetPath - Normalized target directory path
21
+ * @param {boolean} isMacro - Whether to generate a macro template
22
+ * @param {string} version - Semantic version string (e.g. 'v1.0.0')
23
+ * @returns {Promise<{filePath: string, folderName: string}>}
24
+ */
25
+ async create(targetPath, isMacro, version) {
26
+ const folderName = path.basename(targetPath);
27
+ const finalFile = path.join(targetPath, `${folderName}.spec.md`);
28
+
29
+ const template = isMacro
30
+ ? TemplateFactory.macroTemplate(folderName, version)
31
+ : TemplateFactory.microTemplate(folderName, version);
32
+
33
+ await this.#fs.writeFile(finalFile, template);
34
+
35
+ return { filePath: finalFile, folderName };
36
+ }
37
+
38
+ /**
39
+ * Creates a missing .spec.md file for audit purposes.
40
+ * @param {string} codeFilePath - Path to the code file being audited
41
+ * @returns {Promise<{specFilePath: string, codeBaseName: string}>}
42
+ */
43
+ async createIfMissing(codeFilePath) {
44
+ const targetDir = path.dirname(codeFilePath);
45
+ const ext = path.extname(codeFilePath);
46
+ const codeBaseName = path.basename(codeFilePath, ext);
47
+ const specFileName = `${codeBaseName}.spec.md`;
48
+ const specFilePath = path.join(targetDir, specFileName);
49
+
50
+ if (!this.#fs.existsSync(specFilePath)) {
51
+ const version = 'v1.0.0';
52
+ const template = TemplateFactory.auditTemplate(codeBaseName, version);
53
+ await this.#fs.writeFile(specFilePath, template);
54
+ }
55
+
56
+ return { specFilePath, codeBaseName };
57
+ }
58
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Validates that a .spec.md file exists before processing.
3
+ */
4
+ export class SpecValidator {
5
+ /** @type {import('./FileSystemService.js').FileSystemService} */
6
+ #fs;
7
+
8
+ /**
9
+ * @param {import('./FileSystemService.js').FileSystemService} fsService
10
+ */
11
+ constructor(fsService) {
12
+ this.#fs = fsService;
13
+ }
14
+
15
+ /**
16
+ * Validates that a spec file path exists.
17
+ * @param {string} specFilePath
18
+ * @returns {boolean} true if valid
19
+ * @throws {Error} if file does not exist
20
+ */
21
+ validate(specFilePath) {
22
+ if (!this.#fs.existsSync(specFilePath)) {
23
+ throw new Error(`Specification file not found: ${specFilePath}`);
24
+ }
25
+ return true;
26
+ }
27
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Template engine for generating .spec.md file blueprints.
3
+ */
4
+ export class TemplateFactory {
5
+ /**
6
+ * Generates a macro module template (stateDiagram-v2).
7
+ * @param {string} folderName
8
+ * @param {string} version
9
+ * @returns {string}
10
+ */
11
+ static macroTemplate(folderName, version) {
12
+ return (
13
+ `\n# Macro Module: ${folderName} | ${version}\n\n` +
14
+ '```mermaid\n' +
15
+ `%% @spec-version ${version}\n` +
16
+ 'stateDiagram-v2\n' +
17
+ ` [*] --> Initial_${folderName}\n` +
18
+ '```\n\n' +
19
+ '## 3. Audit History\n' +
20
+ '<details>\n' +
21
+ '<summary>Click to expand</summary>\n' +
22
+ '\n\n\n' +
23
+ '</details>\n'
24
+ );
25
+ }
26
+
27
+ /**
28
+ * Generates a micro feature template (graph LR + Decision Matrix).
29
+ * @param {string} folderName
30
+ * @param {string} version
31
+ * @returns {string}
32
+ */
33
+ static microTemplate(folderName, version) {
34
+ return (
35
+ `\n# Specification: ${folderName} | ${version}\n\n` +
36
+ '## 1. Flow Contract (Mermaid)\n' +
37
+ '```mermaid\n' +
38
+ `%% @spec-version ${version}\n` +
39
+ 'graph LR\n' +
40
+ ' A([Start]) --> B[Process]\n' +
41
+ '```\n\n' +
42
+ '## 2. Decision Matrix\n' +
43
+ '| Factor A? | Factor B? | Proposed Action | Decision (Outcome) | Transition State (New Status) |\n' +
44
+ '| :---: | :---: | :--- | :---: | :---: |\n' +
45
+ '| | | | | |\n\n' +
46
+ '## 3. Audit History\n' +
47
+ '<details>\n' +
48
+ '<summary>Click to expand</summary>\n' +
49
+ '\n\n\n' +
50
+ '</details>\n'
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Generates an audit template (graph LR + AuditHistory).
56
+ * @param {string} codeBaseName
57
+ * @param {string} version
58
+ * @returns {string}
59
+ */
60
+ static auditTemplate(codeBaseName, version) {
61
+ return (
62
+ `# Audit: ${codeBaseName} | ${version}\n\n` +
63
+ '## 1. Flow Contract (Mermaid)\n' +
64
+ '```mermaid\n' +
65
+ `%% @spec-version ${version}\n` +
66
+ 'graph LR\n' +
67
+ ' A([Start]) --> B[Process]\n' +
68
+ '```\n\n' +
69
+ '## 2. Decision Matrix\n' +
70
+ '| Condition | Action | Next State |\n' +
71
+ '| :---: | :--- | :---: |\n' +
72
+ '| | | |\n\n' +
73
+ '## 3. Audit History\n' +
74
+ '<details>\n' +
75
+ '<summary>Click to expand</summary>\n' +
76
+ '\n\n\n' +
77
+ '</details>\n'
78
+ );
79
+ }
80
+ }