@schilling.mark.a/software-methodology 1.0.0 → 1.0.1

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.
Files changed (69) hide show
  1. package/.github/copilot-instructions.md +159 -0
  2. package/README.md +172 -6
  3. package/docs/story-map/backbone.md +141 -0
  4. package/docs/story-map/releases/r1-walking-skeleton.md +152 -0
  5. package/docs/story-map/user-tasks/ACT-001-task-001.md +45 -0
  6. package/docs/story-map/user-tasks/ACT-001-task-002.md +48 -0
  7. package/docs/story-map/user-tasks/ACT-002-task-001.md +47 -0
  8. package/docs/story-map/user-tasks/ACT-002-task-002.md +47 -0
  9. package/docs/story-map/user-tasks/ACT-002-task-003.md +46 -0
  10. package/docs/story-map/user-tasks/ACT-003-task-001.md +47 -0
  11. package/docs/story-map/user-tasks/ACT-003-task-002.md +46 -0
  12. package/docs/story-map/user-tasks/ACT-003-task-003.md +49 -0
  13. package/docs/story-map/user-tasks/ACT-003-task-004.md +47 -0
  14. package/docs/story-map/user-tasks/ACT-004-task-001.md +48 -0
  15. package/docs/story-map/user-tasks/ACT-004-task-002.md +49 -0
  16. package/docs/story-map/user-tasks/ACT-004-task-003.md +47 -0
  17. package/docs/story-map/user-tasks/ACT-005-task-001.md +47 -0
  18. package/docs/story-map/user-tasks/ACT-005-task-002.md +48 -0
  19. package/docs/story-map/user-tasks/ACT-005-task-003.md +48 -0
  20. package/docs/story-map/user-tasks/ACT-005-task-004.md +48 -0
  21. package/docs/story-map/user-tasks/ACT-006-task-001.md +47 -0
  22. package/docs/story-map/user-tasks/ACT-006-task-002.md +46 -0
  23. package/docs/story-map/user-tasks/ACT-006-task-003.md +47 -0
  24. package/docs/story-map/user-tasks/ACT-006-task-004.md +46 -0
  25. package/docs/story-map/user-tasks/ACT-007-task-001.md +48 -0
  26. package/docs/story-map/user-tasks/ACT-007-task-002.md +47 -0
  27. package/docs/story-map/user-tasks/ACT-007-task-003.md +47 -0
  28. package/docs/story-map/user-tasks/ACT-007-task-004.md +48 -0
  29. package/docs/story-map/user-tasks/ACT-008-task-001.md +48 -0
  30. package/docs/story-map/user-tasks/ACT-008-task-002.md +48 -0
  31. package/docs/story-map/user-tasks/ACT-008-task-003.md +47 -0
  32. package/docs/story-map/user-tasks/ACT-008-task-004.md +48 -0
  33. package/docs/story-map/walking-skeleton.md +95 -0
  34. package/docs/value-proposition-canvas.md +171 -0
  35. package/features/mcp-server/query-vpc.feature +48 -0
  36. package/features/mcp-server/read-reference.feature +41 -0
  37. package/features/mcp-server/read-skill.feature +33 -0
  38. package/features/mcp-server/search-guidance.feature +42 -0
  39. package/features/mcp-server/suggest-next-step.feature +61 -0
  40. package/features/mcp-server/validate-gherkin.feature +54 -0
  41. package/mcp-server/QUICKSTART.md +172 -0
  42. package/mcp-server/README.md +171 -0
  43. package/mcp-server/dist/index.d.ts +12 -0
  44. package/mcp-server/dist/index.js +296 -0
  45. package/mcp-server/dist/repository.d.ts +59 -0
  46. package/mcp-server/dist/repository.js +211 -0
  47. package/mcp-server/dist/tools/gherkin-validator.d.ts +16 -0
  48. package/mcp-server/dist/tools/gherkin-validator.js +152 -0
  49. package/mcp-server/dist/tools/guidance-searcher.d.ts +11 -0
  50. package/mcp-server/dist/tools/guidance-searcher.js +34 -0
  51. package/mcp-server/dist/tools/next-step-suggester.d.ts +16 -0
  52. package/mcp-server/dist/tools/next-step-suggester.js +210 -0
  53. package/mcp-server/dist/tools/reference-reader.d.ts +17 -0
  54. package/mcp-server/dist/tools/reference-reader.js +57 -0
  55. package/mcp-server/dist/tools/skill-reader.d.ts +17 -0
  56. package/mcp-server/dist/tools/skill-reader.js +38 -0
  57. package/mcp-server/dist/tools/vpc-querier.d.ts +37 -0
  58. package/mcp-server/dist/tools/vpc-querier.js +158 -0
  59. package/mcp-server/package.json +42 -0
  60. package/mcp-server/src/index.ts +331 -0
  61. package/mcp-server/src/repository.ts +254 -0
  62. package/mcp-server/src/tools/gherkin-validator.ts +206 -0
  63. package/mcp-server/src/tools/guidance-searcher.ts +42 -0
  64. package/mcp-server/src/tools/next-step-suggester.ts +243 -0
  65. package/mcp-server/src/tools/reference-reader.ts +71 -0
  66. package/mcp-server/src/tools/skill-reader.ts +47 -0
  67. package/mcp-server/src/tools/vpc-querier.ts +201 -0
  68. package/mcp-server/tsconfig.json +17 -0
  69. package/package.json +8 -2
@@ -0,0 +1,211 @@
1
+ import { promises as fs } from "fs";
2
+ import { join, dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = dirname(__filename);
6
+ /**
7
+ * Repository for accessing methodology files
8
+ */
9
+ export class MethodologyRepository {
10
+ methodologyRoot;
11
+ constructor() {
12
+ // Point to parent directory from mcp-server
13
+ this.methodologyRoot = join(__dirname, "..", "..");
14
+ }
15
+ /**
16
+ * List all available skills
17
+ */
18
+ async listSkills() {
19
+ const skills = [
20
+ "product-strategy",
21
+ "ux-research",
22
+ "story-mapping",
23
+ "bdd-specification",
24
+ "ux-design",
25
+ "ui-design-workflow",
26
+ "ui-design-system",
27
+ "atdd-workflow",
28
+ "clean-code",
29
+ "cicd-pipeline",
30
+ "continuous-improvement",
31
+ ];
32
+ const skillList = [];
33
+ for (const skillName of skills) {
34
+ const skillPath = join(this.methodologyRoot, skillName, "SKILL.md");
35
+ try {
36
+ const content = await fs.readFile(skillPath, "utf-8");
37
+ const description = this.extractDescription(content);
38
+ skillList.push({
39
+ name: skillName,
40
+ displayName: this.toDisplayName(skillName),
41
+ description,
42
+ path: skillPath,
43
+ });
44
+ }
45
+ catch (error) {
46
+ // Skip skills that don't have SKILL.md yet
47
+ console.error(`Warning: Could not read ${skillPath}`);
48
+ }
49
+ }
50
+ return skillList;
51
+ }
52
+ /**
53
+ * Read a skill's main documentation
54
+ */
55
+ async readSkill(skillName) {
56
+ const skillPath = join(this.methodologyRoot, skillName, "SKILL.md");
57
+ return await fs.readFile(skillPath, "utf-8");
58
+ }
59
+ /**
60
+ * List references for a skill
61
+ */
62
+ async listReferences(skillName) {
63
+ const referencesDir = join(this.methodologyRoot, skillName, "references");
64
+ try {
65
+ const files = await fs.readdir(referencesDir);
66
+ return files
67
+ .filter((f) => f.endsWith(".md"))
68
+ .map((f) => ({
69
+ name: f,
70
+ path: join(referencesDir, f),
71
+ skill: skillName,
72
+ }));
73
+ }
74
+ catch (error) {
75
+ return []; // No references directory
76
+ }
77
+ }
78
+ /**
79
+ * Read a reference document
80
+ */
81
+ async readReference(skillName, referenceName) {
82
+ const refPath = join(this.methodologyRoot, skillName, "references", referenceName);
83
+ return await fs.readFile(refPath, "utf-8");
84
+ }
85
+ /**
86
+ * Read VPC content
87
+ */
88
+ async readVPC() {
89
+ const vpcPath = join(this.methodologyRoot, "docs", "value-proposition-canvas.md");
90
+ return await fs.readFile(vpcPath, "utf-8");
91
+ }
92
+ /**
93
+ * Read story map backbone
94
+ */
95
+ async readStoryMap() {
96
+ const storyMapPath = join(this.methodologyRoot, "docs", "story-map", "backbone.md");
97
+ try {
98
+ return await fs.readFile(storyMapPath, "utf-8");
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ /**
105
+ * List feature files
106
+ */
107
+ async listFeatureFiles(projectPath) {
108
+ const featuresDir = projectPath
109
+ ? join(projectPath, "features")
110
+ : join(this.methodologyRoot, "features");
111
+ try {
112
+ const files = await this.walkDirectory(featuresDir);
113
+ return files.filter((f) => f.endsWith(".feature"));
114
+ }
115
+ catch {
116
+ return [];
117
+ }
118
+ }
119
+ /**
120
+ * Search all content
121
+ */
122
+ async searchContent(query) {
123
+ const results = [];
124
+ const skills = await this.listSkills();
125
+ const queryLower = query.toLowerCase();
126
+ for (const skill of skills) {
127
+ const content = await this.readSkill(skill.name);
128
+ const relevance = this.calculateRelevance(content, queryLower);
129
+ if (relevance > 0) {
130
+ results.push({
131
+ file: `${skill.name}/SKILL.md`,
132
+ content: this.extractRelevantSection(content, queryLower),
133
+ relevance,
134
+ });
135
+ }
136
+ const references = await this.listReferences(skill.name);
137
+ for (const ref of references) {
138
+ const refContent = await this.readReference(skill.name, ref.name);
139
+ const refRelevance = this.calculateRelevance(refContent, queryLower);
140
+ if (refRelevance > 0) {
141
+ results.push({
142
+ file: `${skill.name}/references/${ref.name}`,
143
+ content: this.extractRelevantSection(refContent, queryLower),
144
+ relevance: refRelevance,
145
+ });
146
+ }
147
+ }
148
+ }
149
+ return results.sort((a, b) => b.relevance - a.relevance);
150
+ }
151
+ async walkDirectory(dir) {
152
+ const files = [];
153
+ const entries = await fs.readdir(dir, { withFileTypes: true });
154
+ for (const entry of entries) {
155
+ const fullPath = join(dir, entry.name);
156
+ if (entry.isDirectory()) {
157
+ files.push(...(await this.walkDirectory(fullPath)));
158
+ }
159
+ else {
160
+ files.push(fullPath);
161
+ }
162
+ }
163
+ return files;
164
+ }
165
+ extractDescription(content) {
166
+ // Extract description from frontmatter if exists
167
+ const frontmatterMatch = content.match(/---\n.*?description:\s*(.+?)\n/s);
168
+ if (frontmatterMatch) {
169
+ return frontmatterMatch[1].trim();
170
+ }
171
+ // Otherwise get first paragraph
172
+ const lines = content.split("\n");
173
+ for (const line of lines) {
174
+ if (line.trim() && !line.startsWith("#") && !line.startsWith("---")) {
175
+ return line.trim();
176
+ }
177
+ }
178
+ return "";
179
+ }
180
+ toDisplayName(skillName) {
181
+ return skillName
182
+ .split("-")
183
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
184
+ .join(" ");
185
+ }
186
+ calculateRelevance(content, query) {
187
+ const contentLower = content.toLowerCase();
188
+ const words = query.split(/\s+/);
189
+ let score = 0;
190
+ for (const word of words) {
191
+ const count = (contentLower.match(new RegExp(word, "g")) || []).length;
192
+ score += count;
193
+ }
194
+ return score;
195
+ }
196
+ extractRelevantSection(content, query) {
197
+ const lines = content.split("\n");
198
+ const queryWords = query.split(/\s+/);
199
+ for (let i = 0; i < lines.length; i++) {
200
+ const lineLower = lines[i].toLowerCase();
201
+ if (queryWords.some((word) => lineLower.includes(word))) {
202
+ // Extract context around match (5 lines before and after)
203
+ const start = Math.max(0, i - 5);
204
+ const end = Math.min(lines.length, i + 6);
205
+ return lines.slice(start, end).join("\n");
206
+ }
207
+ }
208
+ // Fallback: return first 300 chars
209
+ return content.slice(0, 300) + "...";
210
+ }
211
+ }
@@ -0,0 +1,16 @@
1
+ import { MethodologyRepository } from "../repository.js";
2
+ export declare class GherkinValidator {
3
+ private repository;
4
+ constructor(repository: MethodologyRepository);
5
+ validate(content: string): Promise<{
6
+ content: {
7
+ type: "text";
8
+ text: string;
9
+ }[];
10
+ }>;
11
+ private extractScenarios;
12
+ private validateScenarioStructure;
13
+ private getScenarioStructure;
14
+ private hasSimilarStructures;
15
+ private formatValidationResults;
16
+ }
@@ -0,0 +1,152 @@
1
+ export class GherkinValidator {
2
+ repository;
3
+ constructor(repository) {
4
+ this.repository = repository;
5
+ }
6
+ async validate(content) {
7
+ const result = {
8
+ isValid: true,
9
+ errors: [],
10
+ warnings: [],
11
+ suggestions: [],
12
+ };
13
+ // Check for feature header
14
+ if (!content.includes("Feature:")) {
15
+ result.isValid = false;
16
+ result.errors.push("Missing 'Feature:' declaration");
17
+ }
18
+ // Check for user story (As a... I want... So that...)
19
+ const hasAsA = /As an?/i.test(content);
20
+ const hasIWant = /I want/i.test(content);
21
+ const hasSoThat = /So that/i.test(content);
22
+ if (!hasAsA || !hasIWant || !hasSoThat) {
23
+ result.isValid = false;
24
+ result.errors.push("Missing user story format (As a... I want... So that...)");
25
+ }
26
+ // Check for VPC traceability
27
+ const vpcKeywords = [
28
+ "business value",
29
+ "pain",
30
+ "gain",
31
+ "user need",
32
+ "customer job",
33
+ ];
34
+ const hasSomeTrace = vpcKeywords.some((keyword) => content.toLowerCase().includes(keyword.toLowerCase()));
35
+ if (!hasSomeTrace) {
36
+ result.warnings.push("No clear VPC traceability found. Consider referencing pains/gains in 'So that' clause");
37
+ }
38
+ // Check Given-When-Then structure
39
+ const scenarios = this.extractScenarios(content);
40
+ for (const scenario of scenarios) {
41
+ this.validateScenarioStructure(scenario, result);
42
+ }
43
+ // Check for repeated scenarios (suggest Scenario Outline)
44
+ if (scenarios.length >= 3) {
45
+ const structures = scenarios.map((s) => this.getScenarioStructure(s));
46
+ const hasSimilar = this.hasSimilarStructures(structures);
47
+ if (hasSimilar) {
48
+ result.suggestions.push("Multiple scenarios have similar structure. Consider using Scenario Outline with Examples table. Reference: bdd-specification/gherkin-patterns.md");
49
+ }
50
+ }
51
+ // Format results
52
+ const text = this.formatValidationResults(result);
53
+ return {
54
+ content: [
55
+ {
56
+ type: "text",
57
+ text,
58
+ },
59
+ ],
60
+ };
61
+ }
62
+ extractScenarios(content) {
63
+ const scenarios = [];
64
+ const scenarioRegex = /Scenario(?: Outline)?:(.+?)(?=\n\s*Scenario|$)/gs;
65
+ let match;
66
+ while ((match = scenarioRegex.exec(content)) !== null) {
67
+ scenarios.push(match[0]);
68
+ }
69
+ return scenarios;
70
+ }
71
+ validateScenarioStructure(scenario, result) {
72
+ const lines = scenario.split("\n").map((l) => l.trim());
73
+ // Check for proper tense in Given
74
+ const givenLines = lines.filter((l) => l.startsWith("Given"));
75
+ for (const given of givenLines) {
76
+ if (/\bwill\b|\bshall\b/i.test(given)) {
77
+ result.warnings.push(`Given step should be past tense, not future: "${given}". Reference: Given establishes existing state.`);
78
+ }
79
+ }
80
+ // Check for single When action
81
+ const whenLines = lines.filter((l) => l.startsWith("When"));
82
+ if (whenLines.length > 1) {
83
+ const hasAnd = lines.some((l) => l.startsWith("And") &&
84
+ lines.indexOf(l) > lines.findIndex((l) => l.startsWith("When")));
85
+ if (!hasAnd) {
86
+ result.warnings.push("Multiple When steps should use 'And' for readability");
87
+ }
88
+ }
89
+ // Check for observable outcomes in Then
90
+ const thenLines = lines.filter((l) => l.startsWith("Then"));
91
+ for (const then of thenLines) {
92
+ const nonObservable = [
93
+ "database",
94
+ "queue",
95
+ "log",
96
+ "internal state",
97
+ "cache",
98
+ ];
99
+ for (const keyword of nonObservable) {
100
+ if (then.toLowerCase().includes(keyword)) {
101
+ result.warnings.push(`Then step references internal state: "${then}". Then steps should verify user-observable outcomes only.`);
102
+ }
103
+ }
104
+ }
105
+ }
106
+ getScenarioStructure(scenario) {
107
+ const lines = scenario.split("\n").map((l) => l.trim());
108
+ return lines
109
+ .filter((l) => l.startsWith("Given") || l.startsWith("When") || l.startsWith("Then"))
110
+ .map((l) => l.split(":")[0])
111
+ .join(" -> ");
112
+ }
113
+ hasSimilarStructures(structures) {
114
+ if (structures.length < 3)
115
+ return false;
116
+ const counts = structures.reduce((acc, s) => {
117
+ acc[s] = (acc[s] || 0) + 1;
118
+ return acc;
119
+ }, {});
120
+ return Object.values(counts).some((count) => count >= 3);
121
+ }
122
+ formatValidationResults(result) {
123
+ let text = "# Gherkin Validation Results\n\n";
124
+ if (result.isValid && result.warnings.length === 0 && result.suggestions.length === 0) {
125
+ text += "✅ **Validation PASSED**\n\nYour feature file follows best practices!";
126
+ return text;
127
+ }
128
+ if (!result.isValid) {
129
+ text += "❌ **Validation FAILED**\n\n";
130
+ }
131
+ else {
132
+ text += "⚠️ **Validation PASSED with warnings**\n\n";
133
+ }
134
+ if (result.errors.length > 0) {
135
+ text += "## Errors\n\n";
136
+ text += result.errors.map((e) => `- ❌ ${e}`).join("\n");
137
+ text += "\n\n";
138
+ }
139
+ if (result.warnings.length > 0) {
140
+ text += "## Warnings\n\n";
141
+ text += result.warnings.map((w) => `- ⚠️ ${w}`).join("\n");
142
+ text += "\n\n";
143
+ }
144
+ if (result.suggestions.length > 0) {
145
+ text += "## Suggestions\n\n";
146
+ text += result.suggestions.map((s) => `- 💡 ${s}`).join("\n");
147
+ text += "\n\n";
148
+ }
149
+ text += "\n**Reference:** bdd-specification/references/gherkin-patterns.md";
150
+ return text;
151
+ }
152
+ }
@@ -0,0 +1,11 @@
1
+ import { MethodologyRepository } from "../repository.js";
2
+ export declare class GuidanceSearcher {
3
+ private repository;
4
+ constructor(repository: MethodologyRepository);
5
+ search(query: string): Promise<{
6
+ content: {
7
+ type: "text";
8
+ text: string;
9
+ }[];
10
+ }>;
11
+ }
@@ -0,0 +1,34 @@
1
+ export class GuidanceSearcher {
2
+ repository;
3
+ constructor(repository) {
4
+ this.repository = repository;
5
+ }
6
+ async search(query) {
7
+ const results = await this.repository.searchContent(query);
8
+ if (results.length === 0) {
9
+ const skills = await this.repository.listSkills();
10
+ const skillList = skills.map((s) => s.name).join(", ");
11
+ return {
12
+ content: [
13
+ {
14
+ type: "text",
15
+ text: `No results found for '${query}'.\n\nAvailable skills: ${skillList}`,
16
+ },
17
+ ],
18
+ };
19
+ }
20
+ // Take top 5 results
21
+ const topResults = results.slice(0, 5);
22
+ const text = topResults
23
+ .map((r, i) => `### ${i + 1}. ${r.file} (relevance: ${r.relevance})\n\n${r.content}\n`)
24
+ .join("\n---\n\n");
25
+ return {
26
+ content: [
27
+ {
28
+ type: "text",
29
+ text: `# Search Results for '${query}'\n\n${text}`,
30
+ },
31
+ ],
32
+ };
33
+ }
34
+ }
@@ -0,0 +1,16 @@
1
+ import { MethodologyRepository } from "../repository.js";
2
+ export declare class NextStepSuggester {
3
+ private repository;
4
+ constructor(repository: MethodologyRepository);
5
+ suggest(projectPath?: string): Promise<{
6
+ content: {
7
+ type: "text";
8
+ text: string;
9
+ }[];
10
+ }>;
11
+ private analyzeProjectContext;
12
+ private determineSuggestion;
13
+ private fileExists;
14
+ private hasTestFiles;
15
+ private hasPipelineConfig;
16
+ }
@@ -0,0 +1,210 @@
1
+ import { promises as fs } from "fs";
2
+ import { join } from "path";
3
+ export class NextStepSuggester {
4
+ repository;
5
+ constructor(repository) {
6
+ this.repository = repository;
7
+ }
8
+ async suggest(projectPath) {
9
+ const context = await this.analyzeProjectContext(projectPath);
10
+ const suggestion = this.determineSuggestion(context);
11
+ return {
12
+ content: [
13
+ {
14
+ type: "text",
15
+ text: suggestion,
16
+ },
17
+ ],
18
+ };
19
+ }
20
+ async analyzeProjectContext(projectPath) {
21
+ const docsPath = projectPath ? join(projectPath, "docs") : join(process.cwd(), "docs");
22
+ try {
23
+ const vpcContent = await this.repository.readVPC();
24
+ const storyMapContent = await this.repository.readStoryMap();
25
+ const featureFiles = await this.repository.listFeatureFiles(projectPath);
26
+ return {
27
+ hasVPC: !!vpcContent,
28
+ hasBMC: await this.fileExists(join(docsPath, "business-model-canvas.md")),
29
+ hasStoryMap: !!storyMapContent,
30
+ hasFeatureFiles: featureFiles.length > 0,
31
+ hasTests: await this.hasTestFiles(projectPath),
32
+ testsPassing: false, // TODO: Implement test execution check
33
+ hasPipeline: await this.hasPipelineConfig(projectPath),
34
+ };
35
+ }
36
+ catch {
37
+ return {
38
+ hasVPC: false,
39
+ hasBMC: false,
40
+ hasStoryMap: false,
41
+ hasFeatureFiles: false,
42
+ hasTests: false,
43
+ testsPassing: false,
44
+ hasPipeline: false,
45
+ };
46
+ }
47
+ }
48
+ determineSuggestion(context) {
49
+ if (!context.hasVPC) {
50
+ return `# Next Step: Create Value Proposition Canvas
51
+
52
+ You should start by defining business value using the Value Proposition Canvas.
53
+
54
+ **Why:** The VPC establishes the foundation for all downstream work. It defines customer segments, jobs, pains, and gains that inform every feature decision.
55
+
56
+ **Reference:** product-strategy/SKILL.md
57
+
58
+ **What to create:** /docs/value-proposition-canvas.md
59
+
60
+ The VPC should include:
61
+ - Customer segments (who are your users?)
62
+ - Customer jobs (what are they trying to accomplish?)
63
+ - Pains (what frustrates them?)
64
+ - Gains (what success looks like)
65
+ - Pain relievers (how your product helps)
66
+ - Gain creators (how your product delivers value)`;
67
+ }
68
+ if (!context.hasStoryMap) {
69
+ return `# Next Step: Create Story Map Backbone
70
+
71
+ You have a Value Proposition Canvas. Now organize user activities into a story map.
72
+
73
+ **Why:** The story map backbone organizes work by user activities (derived from VPC customer jobs) rather than technical components.
74
+
75
+ **Reference:** story-mapping/SKILL.md
76
+
77
+ **What to create:** /docs/story-map/backbone.md
78
+
79
+ Each customer job from the VPC becomes one user activity in the backbone.`;
80
+ }
81
+ if (!context.hasFeatureFiles) {
82
+ return `# Next Step: Write Gherkin Scenarios
83
+
84
+ You have a story map. Now specify behavior using concrete examples.
85
+
86
+ **Why:** Gherkin scenarios define "done" before implementation starts. They become executable acceptance tests.
87
+
88
+ **Reference:** bdd-specification/SKILL.md
89
+
90
+ **What to create:** /features/*.feature
91
+
92
+ Each scenario should:
93
+ - Use Given-When-Then format
94
+ - Trace "So that" clause to a VPC gain
95
+ - Specify observable user outcomes`;
96
+ }
97
+ if (!context.hasTests) {
98
+ return `# Next Step: Write Failing Acceptance Test (RED Phase)
99
+
100
+ You have Gherkin scenarios. Now write automated tests that verify them.
101
+
102
+ **Why:** Test-first development defines success criteria before implementation. Tests fail initially because the feature doesn't exist yet.
103
+
104
+ **Reference:** atdd-workflow/red-phase.md
105
+
106
+ **What to do:**
107
+ 1. Pick one Gherkin scenario
108
+ 2. Write automated test that verifies it
109
+ 3. Run test and confirm it fails with clear error message
110
+ 4. Proceed to GREEN phase once RED phase passes`;
111
+ }
112
+ if (context.hasTests && !context.testsPassing) {
113
+ return `# Next Step: Make Tests Pass (GREEN Phase)
114
+
115
+ You have failing tests. Now implement minimum code to make them pass.
116
+
117
+ **Why:** GREEN phase focuses on making it work, not making it perfect. Simplicity first, optimization later.
118
+
119
+ **Reference:** atdd-workflow/green-phase.md
120
+
121
+ **What to do:**
122
+ 1. Write simplest implementation that passes the test
123
+ 2. No premature optimization
124
+ 3. No "future-proofing"
125
+ 4. Run tests until they pass
126
+ 5. Proceed to REFACTOR phase once tests are green`;
127
+ }
128
+ if (context.testsPassing) {
129
+ return `# Next Step: Refactor for Maintainability
130
+
131
+ You have passing tests. Now improve code structure.
132
+
133
+ **Why:** REFACTOR phase is where clean code principles apply. With tests protecting you, safely improve structure.
134
+
135
+ **References:**
136
+ - atdd-workflow/refactor-phase.md
137
+ - clean-code/solid.md
138
+
139
+ **What to do:**
140
+ 1. Keep tests green throughout refactoring
141
+ 2. Apply SOLID principles
142
+ 3. Extract duplications
143
+ 4. Use ubiquitous language
144
+ 5. Reference clean-code patterns as needed`;
145
+ }
146
+ if (!context.hasPipeline) {
147
+ return `# Next Step: Set Up CI/CD Pipeline
148
+
149
+ You have tested code. Now automate deployment.
150
+
151
+ **Why:** Automated pipeline builds, tests, and deploys code without manual steps. Makes frequent delivery safe and easy.
152
+
153
+ **Reference:** cicd-pipeline/SKILL.md
154
+
155
+ **What to create:** Pipeline configuration (e.g., .github/workflows/ci.yml)
156
+
157
+ Pipeline should include:
158
+ - Build stage
159
+ - Test stage (runs all tests)
160
+ - Deploy stage (on successful tests)`;
161
+ }
162
+ return `# Next Step: Measure and Improve
163
+
164
+ You have a complete delivery workflow. Now collect data and iterate.
165
+
166
+ **Why:** Continuous improvement validates that delivered features create expected value and identifies process improvements.
167
+
168
+ **Reference:** continuous-improvement/SKILL.md
169
+
170
+ **What to do:**
171
+ 1. Define success metrics (connects to VPC gains)
172
+ 2. Collect measurement data
173
+ 3. Perform root cause analysis when issues arise
174
+ 4. Update upstream artifacts based on learnings`;
175
+ }
176
+ async fileExists(path) {
177
+ try {
178
+ await fs.access(path);
179
+ return true;
180
+ }
181
+ catch {
182
+ return false;
183
+ }
184
+ }
185
+ async hasTestFiles(projectPath) {
186
+ const testDirs = ["test", "tests", "spec", "__tests__"];
187
+ const basePath = projectPath || process.cwd();
188
+ for (const dir of testDirs) {
189
+ if (await this.fileExists(join(basePath, dir))) {
190
+ return true;
191
+ }
192
+ }
193
+ return false;
194
+ }
195
+ async hasPipelineConfig(projectPath) {
196
+ const pipelineFiles = [
197
+ ".github/workflows",
198
+ ".gitlab-ci.yml",
199
+ "Jenkinsfile",
200
+ ".circleci/config.yml",
201
+ ];
202
+ const basePath = projectPath || process.cwd();
203
+ for (const file of pipelineFiles) {
204
+ if (await this.fileExists(join(basePath, file))) {
205
+ return true;
206
+ }
207
+ }
208
+ return false;
209
+ }
210
+ }
@@ -0,0 +1,17 @@
1
+ import { MethodologyRepository } from "../repository.js";
2
+ export declare class ReferenceReader {
3
+ private repository;
4
+ constructor(repository: MethodologyRepository);
5
+ readReference(referencePath: string): Promise<{
6
+ content: {
7
+ type: "text";
8
+ text: string;
9
+ }[];
10
+ }>;
11
+ listReferences(skillName: string): Promise<{
12
+ content: {
13
+ type: "text";
14
+ text: string;
15
+ }[];
16
+ }>;
17
+ }