mspec 0.0.5 → 0.0.8
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 +18 -13
- package/dist/commands/init.js +37 -20
- package/dist/commands/init.test.js +16 -3
- package/dist/templates/index.js +21 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,16 +16,10 @@ It is designed to work seamlessly alongside your favorite AI coding agents: Clau
|
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
We recommend running `mspec` directly via `npx` so you always get the freshest, most up-to-date prompts for your AI agents when initializing a new project.
|
|
20
20
|
|
|
21
|
-
### Running via npx (No install required)
|
|
22
21
|
```bash
|
|
23
|
-
npx mspec
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
### Global Installation
|
|
27
|
-
```bash
|
|
28
|
-
npm install -g mspec
|
|
22
|
+
npx mspec@latest init
|
|
29
23
|
```
|
|
30
24
|
|
|
31
25
|
---
|
|
@@ -37,11 +31,12 @@ The workflow follows a simple three-step loop: **Initialize -> Plan -> Implement
|
|
|
37
31
|
### Step 1: Initialize the Project
|
|
38
32
|
Run this command in the root of your project:
|
|
39
33
|
```bash
|
|
40
|
-
npx mspec init
|
|
34
|
+
npx mspec@latest init
|
|
41
35
|
```
|
|
42
36
|
- It will prompt you for your preferred AI agent (Claude, Gemini, Cursor, etc.).
|
|
43
37
|
- It will create the `.mspec/specs/` and `.mspec/tasks/` directories.
|
|
44
38
|
- It will automatically inject custom commands into your project (e.g., `.gemini/commands/mspec.plan.toml` or `.cursor/rules/mspec.implement.mdc`) so your AI agent natively understands the framework and provides autocomplete commands like `/mspec.spec`.
|
|
39
|
+
- **Note:** If you already have `.mspec` in your project, running this command will **update** your local AI instructions to the latest version without overwriting your specs or tasks.
|
|
45
40
|
|
|
46
41
|
*(Note: After running `init`, you may need to restart your AI agent session so it can detect the new slash commands).*
|
|
47
42
|
|
|
@@ -53,9 +48,19 @@ Use the native slash command in your AI agent to start drafting a specification.
|
|
|
53
48
|
/mspec.spec Let's create a spec for a new authentication feature.
|
|
54
49
|
```
|
|
55
50
|
- **Context Gathering:** The AI will automatically look at your existing codebase to understand your current architecture before answering.
|
|
56
|
-
- **The Inquiry:** It will
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
- **The Inquiry:** It will not guess. Instead, it will ask you multiple-choice questions to define the core logic, edge cases, and data models.
|
|
52
|
+
|
|
53
|
+
*Example Interaction:*
|
|
54
|
+
> **AI:** Q1: How should we handle session storage?
|
|
55
|
+
> Option A: JWT in HTTP-only cookies (Pros: Secure against XSS. Cons: Harder to invalidate).
|
|
56
|
+
> Option B: Redis-backed sessions (Pros: Easy to revoke. Cons: Requires setting up Redis).
|
|
57
|
+
> Option C: (Custom, please type your answer)
|
|
58
|
+
>
|
|
59
|
+
> **You:** Q1: A
|
|
60
|
+
|
|
61
|
+
- **Approval Checkpoint 1:** After you answer, the AI will ask: *"Are you ready for me to draft the specification based on these answers? (Please reply 'Approved' or 'LGTM')"*
|
|
62
|
+
- **Drafting:** Once approved, it will generate a highly structured `.mspec/specs/001-auth.md` file featuring a Mermaid diagram and an Acceptance Criteria checklist.
|
|
63
|
+
- **Approval Checkpoint 2:** It will output the file path for your review and wait for you to say **"Approved"** or **"LGTM"** again before automatically offering the next command.
|
|
59
64
|
|
|
60
65
|
### Step 3: Scaffold the Plan
|
|
61
66
|
Once you are happy with the spec, use the planning command to break it down.
|
|
@@ -111,4 +116,4 @@ your-project/
|
|
|
111
116
|
│ └── mspec.implement.toml
|
|
112
117
|
├── src/ # Your actual code
|
|
113
118
|
└── package.json
|
|
114
|
-
```
|
|
119
|
+
```
|
package/dist/commands/init.js
CHANGED
|
@@ -12,27 +12,44 @@ const templates_1 = require("../templates");
|
|
|
12
12
|
const { prompt } = require('enquirer');
|
|
13
13
|
async function initCommand() {
|
|
14
14
|
const mspecDir = path_1.default.join(process.cwd(), '.mspec');
|
|
15
|
+
const configPath = path_1.default.join(mspecDir, 'mspec.json');
|
|
16
|
+
let existingAgent = '';
|
|
15
17
|
if (fs_1.default.existsSync(mspecDir)) {
|
|
16
|
-
console.log(chalk_1.default.
|
|
17
|
-
|
|
18
|
+
console.log(chalk_1.default.blue('Existing .mspec directory found. Updating integration files...'));
|
|
19
|
+
if (fs_1.default.existsSync(configPath)) {
|
|
20
|
+
try {
|
|
21
|
+
const config = JSON.parse(fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
22
|
+
if (config && config.agent) {
|
|
23
|
+
existingAgent = config.agent;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
// ignore JSON parse errors
|
|
28
|
+
}
|
|
29
|
+
}
|
|
18
30
|
}
|
|
19
|
-
let
|
|
20
|
-
|
|
21
|
-
response
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
let agent = existingAgent;
|
|
32
|
+
if (!agent) {
|
|
33
|
+
let response;
|
|
34
|
+
try {
|
|
35
|
+
response = await prompt({
|
|
36
|
+
type: 'select',
|
|
37
|
+
name: 'agent',
|
|
38
|
+
message: 'Which AI agent are you using?',
|
|
39
|
+
choices: ['claude', 'gemini', 'cursor', 'opencode', 'zed', 'generic'],
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.log(chalk_1.default.yellow('\nInitialization cancelled.'));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
agent = response.agent;
|
|
27
47
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
48
|
+
// Create directories if they don't exist
|
|
49
|
+
if (!fs_1.default.existsSync(mspecDir)) {
|
|
50
|
+
fs_1.default.mkdirSync(path_1.default.join(mspecDir, 'specs'), { recursive: true });
|
|
51
|
+
fs_1.default.mkdirSync(path_1.default.join(mspecDir, 'tasks'), { recursive: true });
|
|
31
52
|
}
|
|
32
|
-
const agent = response.agent;
|
|
33
|
-
// Create directories
|
|
34
|
-
fs_1.default.mkdirSync(path_1.default.join(mspecDir, 'specs'), { recursive: true });
|
|
35
|
-
fs_1.default.mkdirSync(path_1.default.join(mspecDir, 'tasks'), { recursive: true });
|
|
36
53
|
// Write mspec.json config
|
|
37
54
|
const mspecConfig = {
|
|
38
55
|
agent,
|
|
@@ -41,7 +58,7 @@ async function initCommand() {
|
|
|
41
58
|
tasks: '.mspec/tasks'
|
|
42
59
|
}
|
|
43
60
|
};
|
|
44
|
-
fs_1.default.writeFileSync(
|
|
61
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(mspecConfig, null, 2));
|
|
45
62
|
// Write agent integration files
|
|
46
63
|
const agentTemplates = (0, templates_1.getTemplates)(agent);
|
|
47
64
|
if (agentTemplates.length > 0) {
|
|
@@ -49,11 +66,11 @@ async function initCommand() {
|
|
|
49
66
|
const targetDir = path_1.default.join(process.cwd(), template.dir);
|
|
50
67
|
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
51
68
|
fs_1.default.writeFileSync(path_1.default.join(targetDir, template.file), template.content);
|
|
52
|
-
console.log(chalk_1.default.green(`
|
|
69
|
+
console.log(chalk_1.default.green(`Updated integration file for ${agent} at ${path_1.default.join(template.dir, template.file)}`));
|
|
53
70
|
}
|
|
54
71
|
}
|
|
55
72
|
else {
|
|
56
73
|
console.log(chalk_1.default.yellow(`No specific integration template found for ${agent}. Setup completed with generic settings.`));
|
|
57
74
|
}
|
|
58
|
-
console.log(chalk_1.default.green('mspec initialized successfully!'));
|
|
75
|
+
console.log(chalk_1.default.green('mspec initialized/updated successfully!'));
|
|
59
76
|
}
|
|
@@ -53,13 +53,26 @@ describe('initCommand', () => {
|
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
});
|
|
56
|
-
it('should
|
|
56
|
+
it('should update integration files if .mspec directory already exists', async () => {
|
|
57
57
|
const mspecDir = path_1.default.join(tmpDir, '.mspec');
|
|
58
58
|
fs_1.default.mkdirSync(mspecDir);
|
|
59
|
+
fs_1.default.mkdirSync(path_1.default.join(mspecDir, 'specs'));
|
|
60
|
+
fs_1.default.mkdirSync(path_1.default.join(mspecDir, 'tasks'));
|
|
61
|
+
fs_1.default.writeFileSync(path_1.default.join(mspecDir, 'mspec.json'), JSON.stringify({ agent: 'cursor' }));
|
|
59
62
|
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
63
|
+
mockPrompt.mockRejectedValueOnce(new Error('Should not prompt'));
|
|
60
64
|
await (0, init_1.initCommand)();
|
|
61
|
-
expect(
|
|
62
|
-
expect(
|
|
65
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Existing .mspec directory found. Updating integration files...'));
|
|
66
|
+
expect(fs_1.default.existsSync(path_1.default.join(tmpDir, '.cursor/rules/mspec.spec.mdc'))).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
it('should prompt if .mspec directory exists but mspec.json is missing or invalid', async () => {
|
|
69
|
+
const mspecDir = path_1.default.join(tmpDir, '.mspec');
|
|
70
|
+
fs_1.default.mkdirSync(mspecDir);
|
|
71
|
+
mockPrompt.mockResolvedValueOnce({ agent: 'gemini' });
|
|
72
|
+
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
73
|
+
await (0, init_1.initCommand)();
|
|
74
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Existing .mspec directory found. Updating integration files...'));
|
|
75
|
+
expect(fs_1.default.existsSync(path_1.default.join(tmpDir, '.gemini/commands/mspec.spec.toml'))).toBe(true);
|
|
63
76
|
});
|
|
64
77
|
it('should handle prompt cancellation gracefully', async () => {
|
|
65
78
|
mockPrompt.mockRejectedValueOnce(new Error('User cancelled'));
|
package/dist/templates/index.js
CHANGED
|
@@ -13,16 +13,27 @@ PHASE 0: CONTEXT GATHERING
|
|
|
13
13
|
|
|
14
14
|
PHASE 1: INQUIRY
|
|
15
15
|
2. DO NOT generate the specification file immediately.
|
|
16
|
-
3. Ask the user 3
|
|
16
|
+
3. Ask the user between 3 to 15 targeted questions to clarify the feature, depending on the feature's ambiguity. Incorporate any context you found in Phase 0.
|
|
17
|
+
4. CRITICAL: Format EVERY question as a multiple-choice selection. Provide exactly 2 recommended options (weighing pros and cons based on the current codebase) and 1 option for a custom answer.
|
|
18
|
+
Use this exact format for every question:
|
|
19
|
+
|
|
20
|
+
Q[Number]: [Your Question]
|
|
21
|
+
Option A: [Recommendation 1] (Pros: ... Cons: ...)
|
|
22
|
+
Option B: [Recommendation 2] (Pros: ... Cons: ...)
|
|
23
|
+
Option C: (Custom, please type your answer)
|
|
24
|
+
|
|
25
|
+
5. To build a strong spec, your questions must extract:
|
|
17
26
|
- Core Objective: What is the exact goal and business logic?
|
|
18
27
|
- Edge Cases & Error Handling: What happens on failure, empty states, or invalid input?
|
|
19
28
|
- Data Structures: What exact fields, types, and constraints are required?
|
|
20
29
|
- Dependencies: Does this rely on external APIs, existing UI components, or specific libraries?
|
|
21
|
-
|
|
30
|
+
6. Wait for the user to answer (e.g., "Q1: A, Q2: C - use Redis instead").
|
|
31
|
+
7. If the user provides extra context or wants to discuss further, engage in the discussion and revise your understanding.
|
|
32
|
+
8. CRITICAL: Before moving to Phase 2, you MUST ask: "Are you ready for me to draft the specification based on these answers? (Please reply 'Approved' or 'LGTM')". DO NOT proceed until you get explicit approval.
|
|
22
33
|
|
|
23
34
|
PHASE 2: DRAFTING
|
|
24
|
-
|
|
25
|
-
|
|
35
|
+
9. Once Phase 1 is approved, generate the spec file in .mspec/specs/ using the exact filename requested or a logical slug (e.g., 001-auth.md).
|
|
36
|
+
10. The spec MUST strictly follow this structure:
|
|
26
37
|
# Spec: [Feature Name]
|
|
27
38
|
## 1. Goal & Context
|
|
28
39
|
(Clear explanation of the feature and business value)
|
|
@@ -34,12 +45,13 @@ PHASE 2: DRAFTING
|
|
|
34
45
|
(Explicit list of what can go wrong and how the system should react)
|
|
35
46
|
## 5. Acceptance Criteria
|
|
36
47
|
(Checklist of what must be true for this feature to be considered complete)
|
|
48
|
+
11. Output the exact path to the generated spec file so the user can easily open it.
|
|
49
|
+
12. CRITICAL: Stop and ask: "Please review the drafted spec file. Should I finalize this phase? (Please reply 'Approved' or 'LGTM')".
|
|
50
|
+
13. If the user provides feedback, revise the spec file accordingly and ask for approval again. DO NOT proceed to Phase 3 until explicit approval is given.
|
|
37
51
|
|
|
38
|
-
PHASE 3: REVIEW
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
9. Once approved, output the exact path to the generated spec file so the user can easily open it.
|
|
42
|
-
10. Finally, offer to move to the next phase by asking: "Would you like me to generate the implementation tasks now using /mspec.plan [spec-name]?"`
|
|
52
|
+
PHASE 3: REVIEW & HANDOFF
|
|
53
|
+
14. Once the drafted spec is approved, the specification phase is complete.
|
|
54
|
+
15. Finally, offer to move to the next step by asking: "Would you like me to generate the implementation tasks now using /mspec.plan [spec-name]?"`
|
|
43
55
|
},
|
|
44
56
|
'mspec.plan': {
|
|
45
57
|
desc: 'Plan tasks for an existing spec',
|