specsmd 0.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 (83) hide show
  1. package/README.md +300 -0
  2. package/bin/cli.js +21 -0
  3. package/flows/aidlc/README.md +372 -0
  4. package/flows/aidlc/agents/construction-agent.md +81 -0
  5. package/flows/aidlc/agents/inception-agent.md +95 -0
  6. package/flows/aidlc/agents/master-agent.md +61 -0
  7. package/flows/aidlc/agents/operations-agent.md +89 -0
  8. package/flows/aidlc/commands/construction-agent.md +63 -0
  9. package/flows/aidlc/commands/inception-agent.md +55 -0
  10. package/flows/aidlc/commands/master-agent.md +47 -0
  11. package/flows/aidlc/commands/operations-agent.md +77 -0
  12. package/flows/aidlc/context-config.yaml +41 -0
  13. package/flows/aidlc/memory-bank.yaml +104 -0
  14. package/flows/aidlc/quick-start.md +315 -0
  15. package/flows/aidlc/skills/construction/bolt-list.md +163 -0
  16. package/flows/aidlc/skills/construction/bolt-replan.md +343 -0
  17. package/flows/aidlc/skills/construction/bolt-start.md +289 -0
  18. package/flows/aidlc/skills/construction/bolt-status.md +185 -0
  19. package/flows/aidlc/skills/construction/navigator.md +196 -0
  20. package/flows/aidlc/skills/inception/bolt-plan.md +338 -0
  21. package/flows/aidlc/skills/inception/context.md +171 -0
  22. package/flows/aidlc/skills/inception/intent-create.md +211 -0
  23. package/flows/aidlc/skills/inception/intent-list.md +124 -0
  24. package/flows/aidlc/skills/inception/navigator.md +207 -0
  25. package/flows/aidlc/skills/inception/requirements.md +227 -0
  26. package/flows/aidlc/skills/inception/review.md +248 -0
  27. package/flows/aidlc/skills/inception/story-create.md +304 -0
  28. package/flows/aidlc/skills/inception/units.md +271 -0
  29. package/flows/aidlc/skills/master/analyze-context.md +132 -0
  30. package/flows/aidlc/skills/master/answer-question.md +141 -0
  31. package/flows/aidlc/skills/master/explain-flow.md +146 -0
  32. package/flows/aidlc/skills/master/project-init.md +281 -0
  33. package/flows/aidlc/skills/master/route-request.md +126 -0
  34. package/flows/aidlc/skills/operations/build.md +237 -0
  35. package/flows/aidlc/skills/operations/deploy.md +259 -0
  36. package/flows/aidlc/skills/operations/monitor.md +265 -0
  37. package/flows/aidlc/skills/operations/navigator.md +209 -0
  38. package/flows/aidlc/skills/operations/verify.md +224 -0
  39. package/flows/aidlc/templates/construction/bolt-template.md +193 -0
  40. package/flows/aidlc/templates/construction/bolt-types/bdd-construction-bolt.md +250 -0
  41. package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +49 -0
  42. package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +55 -0
  43. package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +67 -0
  44. package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +62 -0
  45. package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt.md +528 -0
  46. package/flows/aidlc/templates/construction/bolt-types/simple-construction-bolt.md +273 -0
  47. package/flows/aidlc/templates/construction/bolt-types/spike-bolt.md +240 -0
  48. package/flows/aidlc/templates/construction/bolt-types/tdd-construction-bolt.md +259 -0
  49. package/flows/aidlc/templates/construction/construction-log-template.md +129 -0
  50. package/flows/aidlc/templates/construction/standards/coding-standards.md +29 -0
  51. package/flows/aidlc/templates/construction/standards/system-architecture.md +22 -0
  52. package/flows/aidlc/templates/construction/standards/tech-stack.md +19 -0
  53. package/flows/aidlc/templates/inception/inception-log-template.md +134 -0
  54. package/flows/aidlc/templates/inception/project/README.md +55 -0
  55. package/flows/aidlc/templates/inception/requirements-template.md +144 -0
  56. package/flows/aidlc/templates/inception/stories-template.md +38 -0
  57. package/flows/aidlc/templates/inception/story-template.md +147 -0
  58. package/flows/aidlc/templates/inception/system-context-template.md +29 -0
  59. package/flows/aidlc/templates/inception/unit-brief-template.md +177 -0
  60. package/flows/aidlc/templates/inception/units-template.md +52 -0
  61. package/flows/aidlc/templates/standards/catalog.yaml +345 -0
  62. package/flows/aidlc/templates/standards/coding-standards.guide.md +553 -0
  63. package/flows/aidlc/templates/standards/data-stack.guide.md +162 -0
  64. package/flows/aidlc/templates/standards/tech-stack.guide.md +280 -0
  65. package/lib/InstallerFactory.js +36 -0
  66. package/lib/cli-utils.js +372 -0
  67. package/lib/constants.js +31 -0
  68. package/lib/installer.js +314 -0
  69. package/lib/installers/AntigravityInstaller.js +22 -0
  70. package/lib/installers/ClaudeInstaller.js +85 -0
  71. package/lib/installers/ClineInstaller.js +21 -0
  72. package/lib/installers/CodexInstaller.js +21 -0
  73. package/lib/installers/CopilotInstaller.js +113 -0
  74. package/lib/installers/CursorInstaller.js +63 -0
  75. package/lib/installers/GeminiInstaller.js +75 -0
  76. package/lib/installers/KiroInstaller.js +22 -0
  77. package/lib/installers/OpenCodeInstaller.js +22 -0
  78. package/lib/installers/RooInstaller.js +22 -0
  79. package/lib/installers/ToolInstaller.js +73 -0
  80. package/lib/installers/WindsurfInstaller.js +76 -0
  81. package/lib/markdown-validator.ts +175 -0
  82. package/lib/yaml-validator.ts +99 -0
  83. package/package.json +65 -0
@@ -0,0 +1,75 @@
1
+ const ToolInstaller = require('./ToolInstaller');
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const CLIUtils = require('../cli-utils');
5
+ const { theme } = CLIUtils;
6
+
7
+ class GeminiInstaller extends ToolInstaller {
8
+ get key() {
9
+ return 'gemini';
10
+ }
11
+
12
+ get name() {
13
+ return 'Gemini CLI';
14
+ }
15
+
16
+ get commandsDir() {
17
+ return path.join('.gemini', 'commands');
18
+ }
19
+
20
+ get detectPath() {
21
+ return '.gemini';
22
+ }
23
+
24
+ /**
25
+ * Override to convert markdown to TOML format for Gemini CLI
26
+ */
27
+ async installCommands(flowPath, config) {
28
+ const targetDir = this.commandsDir;
29
+ console.log(theme.dim(` Installing commands to ${targetDir}/...`));
30
+ await fs.ensureDir(targetDir);
31
+
32
+ const sourceDir = path.join(flowPath, 'commands');
33
+
34
+ if (!await fs.pathExists(sourceDir)) {
35
+ console.log(theme.dim(` No commands folder found at ${sourceDir}`));
36
+ return [];
37
+ }
38
+
39
+ const files = await fs.readdir(sourceDir);
40
+ const installedFiles = [];
41
+
42
+ for (const file of files) {
43
+ if (file.endsWith('.md')) {
44
+ const sourcePath = path.join(sourceDir, file);
45
+ const prefix = (config && config.command && config.command.prefix) ? `${config.command.prefix}-` : '';
46
+
47
+ // Transform .md to .toml for Gemini CLI
48
+ const targetFileName = `specsmd-${prefix}${file}`.replace(/\.md$/, '.toml');
49
+ const targetPath = path.join(targetDir, targetFileName);
50
+
51
+ // Read source content
52
+ const content = await fs.readFile(sourcePath, 'utf8');
53
+
54
+ // Extract description from filename (e.g., "master-agent" -> "Master Agent")
55
+ const agentName = file.replace(/\.md$/, '').replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
56
+
57
+ // Convert to TOML format
58
+ const tomlContent = `description = "specsmd ${agentName}"
59
+ prompt = """
60
+ ${content}
61
+ """`;
62
+
63
+ await fs.writeFile(targetPath, tomlContent);
64
+ installedFiles.push(targetFileName);
65
+ }
66
+ }
67
+
68
+ if (installedFiles.length > 0) {
69
+ CLIUtils.displayStatus('', `Installed ${installedFiles.length} commands for ${this.name}`, 'success');
70
+ }
71
+ return installedFiles;
72
+ }
73
+ }
74
+
75
+ module.exports = GeminiInstaller;
@@ -0,0 +1,22 @@
1
+ const ToolInstaller = require('./ToolInstaller');
2
+ const path = require('path');
3
+
4
+ class KiroInstaller extends ToolInstaller {
5
+ get key() {
6
+ return 'kiro';
7
+ }
8
+
9
+ get name() {
10
+ return 'Kiro CLI';
11
+ }
12
+
13
+ get commandsDir() {
14
+ return path.join('.kiro', 'steering');
15
+ }
16
+
17
+ get detectPath() {
18
+ return '.kiro';
19
+ }
20
+ }
21
+
22
+ module.exports = KiroInstaller;
@@ -0,0 +1,22 @@
1
+ const ToolInstaller = require('./ToolInstaller');
2
+ const path = require('path');
3
+
4
+ class OpenCodeInstaller extends ToolInstaller {
5
+ get key() {
6
+ return 'opencode';
7
+ }
8
+
9
+ get name() {
10
+ return 'OpenCode';
11
+ }
12
+
13
+ get commandsDir() {
14
+ return path.join('.opencode', 'agent');
15
+ }
16
+
17
+ get detectPath() {
18
+ return '.opencode';
19
+ }
20
+ }
21
+
22
+ module.exports = OpenCodeInstaller;
@@ -0,0 +1,22 @@
1
+ const ToolInstaller = require('./ToolInstaller');
2
+ const path = require('path');
3
+
4
+ class RooInstaller extends ToolInstaller {
5
+ get key() {
6
+ return 'roo';
7
+ }
8
+
9
+ get name() {
10
+ return 'Roo Code';
11
+ }
12
+
13
+ get commandsDir() {
14
+ return path.join('.roo', 'commands');
15
+ }
16
+
17
+ get detectPath() {
18
+ return '.roo';
19
+ }
20
+ }
21
+
22
+ module.exports = RooInstaller;
@@ -0,0 +1,73 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const CLIUtils = require('../cli-utils');
4
+ const { theme } = CLIUtils;
5
+
6
+ /**
7
+ * Base class for Agentic Coding Tool Installers
8
+ */
9
+ class ToolInstaller {
10
+ constructor() {
11
+ if (this.constructor === ToolInstaller) {
12
+ throw new Error("Abstract classes can't be instantiated.");
13
+ }
14
+ }
15
+
16
+ get key() {
17
+ throw new Error("Method 'key' must be implemented.");
18
+ }
19
+
20
+ get name() {
21
+ throw new Error("Method 'name' must be implemented.");
22
+ }
23
+
24
+ get commandsDir() {
25
+ throw new Error("Method 'commandsDir' must be implemented.");
26
+ }
27
+
28
+ get detectPath() {
29
+ throw new Error("Method 'detectPath' must be implemented.");
30
+ }
31
+
32
+ async detect() {
33
+ return await fs.pathExists(this.detectPath);
34
+ }
35
+
36
+ async installCommands(flowPath, config) {
37
+ const targetCommandsDir = this.commandsDir;
38
+ console.log(theme.dim(` Installing commands to ${targetCommandsDir}/...`));
39
+ await fs.ensureDir(targetCommandsDir);
40
+
41
+ const commandsSourceDir = path.join(flowPath, 'commands');
42
+
43
+ if (!await fs.pathExists(commandsSourceDir)) {
44
+ console.log(theme.warning(` No commands folder found at ${commandsSourceDir}`));
45
+ return [];
46
+ }
47
+
48
+ const commandFiles = await fs.readdir(commandsSourceDir);
49
+ const installedFiles = [];
50
+
51
+ for (const cmdFile of commandFiles) {
52
+ if (cmdFile.endsWith('.md')) {
53
+ try {
54
+ const sourcePath = path.join(commandsSourceDir, cmdFile);
55
+ const prefix = (config && config.command && config.command.prefix) ? `${config.command.prefix}-` : '';
56
+ const targetFileName = `specsmd-${prefix}${cmdFile}`;
57
+ const targetPath = path.join(targetCommandsDir, targetFileName);
58
+
59
+ const content = await fs.readFile(sourcePath, 'utf8');
60
+ await fs.outputFile(targetPath, content, 'utf8');
61
+ installedFiles.push(targetFileName);
62
+ } catch (err) {
63
+ console.log(theme.warning(` Failed to install ${cmdFile}: ${err.message}`));
64
+ }
65
+ }
66
+ }
67
+
68
+ CLIUtils.displayStatus('', `Installed ${installedFiles.length} commands for ${this.name}`, 'success');
69
+ return installedFiles;
70
+ }
71
+ }
72
+
73
+ module.exports = ToolInstaller;
@@ -0,0 +1,76 @@
1
+ const ToolInstaller = require('./ToolInstaller');
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const CLIUtils = require('../cli-utils');
5
+ const { theme } = CLIUtils;
6
+
7
+ class WindsurfInstaller extends ToolInstaller {
8
+ get key() {
9
+ return 'windsurf';
10
+ }
11
+
12
+ get name() {
13
+ return 'Windsurf';
14
+ }
15
+
16
+ get commandsDir() {
17
+ return path.join('.windsurf', 'workflows');
18
+ }
19
+
20
+ get detectPath() {
21
+ return '.windsurf';
22
+ }
23
+
24
+ /**
25
+ * Override to add frontmatter for Windsurf workflows
26
+ */
27
+ async installCommands(flowPath, config) {
28
+ const targetCommandsDir = this.commandsDir;
29
+ console.log(theme.dim(` Installing workflows to ${targetCommandsDir}/...`));
30
+ await fs.ensureDir(targetCommandsDir);
31
+
32
+ const commandsSourceDir = path.join(flowPath, 'commands');
33
+
34
+ if (!await fs.pathExists(commandsSourceDir)) {
35
+ console.log(theme.warning(` No commands folder found at ${commandsSourceDir}`));
36
+ return [];
37
+ }
38
+
39
+ const commandFiles = await fs.readdir(commandsSourceDir);
40
+ const installedFiles = [];
41
+
42
+ for (const cmdFile of commandFiles) {
43
+ if (cmdFile.endsWith('.md')) {
44
+ const sourcePath = path.join(commandsSourceDir, cmdFile);
45
+ const prefix = (config && config.command && config.command.prefix) ? `${config.command.prefix}-` : '';
46
+
47
+ const targetFileName = `specsmd-${prefix}${cmdFile}`;
48
+ const targetPath = path.join(targetCommandsDir, targetFileName);
49
+
50
+ // Extract agent name from target filename (e.g., "specsmd-master-agent.md" -> "specsmd-master-agent")
51
+ const agentName = targetFileName.replace(/\.md$/, '');
52
+
53
+ // Read source content and add Windsurf frontmatter
54
+ let content = await fs.readFile(sourcePath, 'utf8');
55
+
56
+ // Add Windsurf-specific frontmatter if not present
57
+ if (!content.startsWith('---')) {
58
+ const frontmatter = `---
59
+ description: ${agentName}
60
+ ---
61
+
62
+ `;
63
+ content = frontmatter + content;
64
+ }
65
+
66
+ await fs.writeFile(targetPath, content);
67
+ installedFiles.push(targetFileName);
68
+ }
69
+ }
70
+
71
+ CLIUtils.displayStatus('', `Installed ${installedFiles.length} workflows for ${this.name}`, 'success');
72
+ return installedFiles;
73
+ }
74
+ }
75
+
76
+ module.exports = WindsurfInstaller;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Template-based markdown validator
3
+ * Validates markdown files against YAML schema definitions
4
+ */
5
+
6
+ import { unified } from 'unified';
7
+ import remarkParse from 'remark-parse';
8
+ import { visit } from 'unist-util-visit';
9
+ import * as fs from 'fs';
10
+ import * as yaml from 'js-yaml';
11
+ import type { Root, Heading, Text } from 'mdast';
12
+
13
+ // Schema types
14
+ export interface SectionRequirement {
15
+ heading: string;
16
+ children?: string[];
17
+ }
18
+
19
+ export interface MarkdownSchema {
20
+ name: string;
21
+ description: string;
22
+ file_pattern: string;
23
+ required_sections: SectionRequirement[];
24
+ optional_sections?: SectionRequirement[];
25
+ rules?: {
26
+ min_content_length?: number;
27
+ must_start_with_h1?: boolean;
28
+ };
29
+ }
30
+
31
+ export interface ValidationResult {
32
+ valid: boolean;
33
+ errors: string[];
34
+ warnings: string[];
35
+ file?: string;
36
+ }
37
+
38
+ /**
39
+ * Extract all headings from markdown AST
40
+ */
41
+ function extractHeadings(tree: Root): { text: string; depth: number }[] {
42
+ const headings: { text: string; depth: number }[] = [];
43
+
44
+ visit(tree, 'heading', (node: Heading) => {
45
+ const text = node.children
46
+ .filter((child): child is Text => child.type === 'text')
47
+ .map(child => child.value)
48
+ .join('');
49
+ headings.push({ text, depth: node.depth });
50
+ });
51
+
52
+ return headings;
53
+ }
54
+
55
+ /**
56
+ * Normalize heading for comparison
57
+ */
58
+ function normalizeHeading(heading: string): string {
59
+ return heading.toLowerCase().replace(/^#+\s*/, '').trim();
60
+ }
61
+
62
+ /**
63
+ * Check if headings contain a section
64
+ */
65
+ function hasSection(
66
+ headings: { text: string; depth: number }[],
67
+ sectionPattern: string
68
+ ): boolean {
69
+ const normalized = normalizeHeading(sectionPattern);
70
+ return headings.some(h => normalizeHeading(h.text).includes(normalized));
71
+ }
72
+
73
+ /**
74
+ * Validate markdown content against a schema
75
+ */
76
+ export function validateMarkdown(
77
+ content: string,
78
+ schema: MarkdownSchema,
79
+ fileName?: string
80
+ ): ValidationResult {
81
+ const errors: string[] = [];
82
+ const warnings: string[] = [];
83
+ const prefix = fileName ? `${fileName}: ` : '';
84
+
85
+ // Parse markdown
86
+ const tree = unified().use(remarkParse).parse(content) as Root;
87
+ const headings = extractHeadings(tree);
88
+
89
+ // Check required sections
90
+ for (const req of schema.required_sections) {
91
+ const sectionExists = hasSection(headings, req.heading);
92
+
93
+ if (!sectionExists) {
94
+ errors.push(`${prefix}Missing required section "${req.heading}"`);
95
+ } else if (req.children) {
96
+ // Check child sections
97
+ for (const child of req.children) {
98
+ if (!hasSection(headings, child)) {
99
+ errors.push(`${prefix}Missing required subsection "${child}"`);
100
+ }
101
+ }
102
+ }
103
+ }
104
+
105
+ // Check optional sections (warnings only)
106
+ if (schema.optional_sections) {
107
+ for (const opt of schema.optional_sections) {
108
+ if (!hasSection(headings, opt.heading)) {
109
+ warnings.push(`${prefix}Missing optional section "${opt.heading}"`);
110
+ }
111
+ }
112
+ }
113
+
114
+ // Check rules
115
+ if (schema.rules) {
116
+ if (schema.rules.min_content_length && content.length < schema.rules.min_content_length) {
117
+ errors.push(`${prefix}Content too short (${content.length} < ${schema.rules.min_content_length})`);
118
+ }
119
+
120
+ if (schema.rules.must_start_with_h1) {
121
+ const firstHeading = headings[0];
122
+ if (!firstHeading || firstHeading.depth !== 1) {
123
+ errors.push(`${prefix}Must start with H1 heading`);
124
+ }
125
+ }
126
+ }
127
+
128
+ return {
129
+ valid: errors.length === 0,
130
+ errors,
131
+ warnings,
132
+ file: fileName,
133
+ };
134
+ }
135
+
136
+ /**
137
+ * Load a schema from YAML file
138
+ */
139
+ export function loadSchema(schemaPath: string): MarkdownSchema {
140
+ const content = fs.readFileSync(schemaPath, 'utf-8');
141
+ return yaml.load(content) as MarkdownSchema;
142
+ }
143
+
144
+ /**
145
+ * Validate a file against a schema
146
+ */
147
+ export function validateFile(
148
+ filePath: string,
149
+ schema: MarkdownSchema
150
+ ): ValidationResult {
151
+ const content = fs.readFileSync(filePath, 'utf-8');
152
+ return validateMarkdown(content, schema, filePath);
153
+ }
154
+
155
+ /**
156
+ * Validate multiple files against a schema
157
+ */
158
+ export function validateFiles(
159
+ filePaths: string[],
160
+ schema: MarkdownSchema
161
+ ): { results: ValidationResult[]; summary: ValidationResult } {
162
+ const results = filePaths.map(fp => validateFile(fp, schema));
163
+
164
+ const allErrors = results.flatMap(r => r.errors);
165
+ const allWarnings = results.flatMap(r => r.warnings);
166
+
167
+ return {
168
+ results,
169
+ summary: {
170
+ valid: allErrors.length === 0,
171
+ errors: allErrors,
172
+ warnings: allWarnings,
173
+ },
174
+ };
175
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Simple YAML config validator
3
+ * Validates required keys and structure without strict schema
4
+ */
5
+
6
+ import * as fs from 'fs';
7
+ import * as yaml from 'js-yaml';
8
+
9
+ export interface YamlSchema {
10
+ name: string;
11
+ description?: string;
12
+ required_keys: string[];
13
+ nested_required?: Record<string, string[]>;
14
+ }
15
+
16
+ export interface ValidationResult {
17
+ valid: boolean;
18
+ errors: string[];
19
+ file?: string;
20
+ }
21
+
22
+ /**
23
+ * Check if an object has a key (supports dot notation)
24
+ */
25
+ function hasKey(obj: Record<string, unknown>, key: string): boolean {
26
+ const parts = key.split('.');
27
+ let current: unknown = obj;
28
+
29
+ for (const part of parts) {
30
+ if (current === null || current === undefined || typeof current !== 'object') {
31
+ return false;
32
+ }
33
+ if (!(part in (current as Record<string, unknown>))) {
34
+ return false;
35
+ }
36
+ current = (current as Record<string, unknown>)[part];
37
+ }
38
+
39
+ return true;
40
+ }
41
+
42
+ /**
43
+ * Validate YAML content against a schema
44
+ */
45
+ export function validateYaml(
46
+ content: Record<string, unknown>,
47
+ schema: YamlSchema,
48
+ fileName?: string
49
+ ): ValidationResult {
50
+ const errors: string[] = [];
51
+ const prefix = fileName ? `${fileName}: ` : '';
52
+
53
+ // Check required keys
54
+ for (const key of schema.required_keys) {
55
+ if (!hasKey(content, key)) {
56
+ errors.push(`${prefix}Missing required key "${key}"`);
57
+ }
58
+ }
59
+
60
+ // Check nested required keys
61
+ if (schema.nested_required) {
62
+ for (const [parent, children] of Object.entries(schema.nested_required)) {
63
+ if (hasKey(content, parent)) {
64
+ for (const child of children) {
65
+ const fullKey = `${parent}.${child}`;
66
+ if (!hasKey(content, fullKey)) {
67
+ errors.push(`${prefix}Missing required key "${fullKey}"`);
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ return {
75
+ valid: errors.length === 0,
76
+ errors,
77
+ file: fileName,
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Validate a YAML file against a schema
83
+ */
84
+ export function validateYamlFile(
85
+ filePath: string,
86
+ schema: YamlSchema
87
+ ): ValidationResult {
88
+ const content = fs.readFileSync(filePath, 'utf-8');
89
+ const parsed = yaml.load(content) as Record<string, unknown>;
90
+ return validateYaml(parsed, schema, filePath);
91
+ }
92
+
93
+ /**
94
+ * Load a schema from YAML file
95
+ */
96
+ export function loadYamlSchema(schemaPath: string): YamlSchema {
97
+ const content = fs.readFileSync(schemaPath, 'utf-8');
98
+ return yaml.load(content) as YamlSchema;
99
+ }
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "specsmd",
3
+ "version": "0.0.1",
4
+ "description": "Multi-agent orchestration system for AI-native software development. Delivers AI-DLC, Agile, and custom SDLC flows as markdown-based agent systems.",
5
+ "main": "lib/installer.js",
6
+ "bin": {
7
+ "specsmd": "./bin/cli.js"
8
+ },
9
+ "scripts": {
10
+ "test": "vitest run",
11
+ "test:watch": "vitest",
12
+ "test:schema": "vitest run __tests__/unit/schema-validation/",
13
+ "lint:md": "markdownlint 'specs/**/*.md' 'flows/**/*.md' --config ../.markdownlint.yaml",
14
+ "lint:md:fix": "markdownlint 'specs/**/*.md' 'flows/**/*.md' --config ../.markdownlint.yaml --fix",
15
+ "validate": "npm run test:schema && npm run lint:md"
16
+ },
17
+ "keywords": [
18
+ "ai-dlc",
19
+ "agile",
20
+ "sdlc",
21
+ "claude-code",
22
+ "cursor",
23
+ "copilot",
24
+ "ai-native",
25
+ "agents",
26
+ "workflow"
27
+ ],
28
+ "author": "specsmd team",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/fabriqaai/specsmd.git"
33
+ },
34
+ "files": [
35
+ "bin/",
36
+ "flows/",
37
+ "lib/",
38
+ "README.md"
39
+ ],
40
+ "dependencies": {
41
+ "chalk": "^4.1.2",
42
+ "commander": "^11.1.0",
43
+ "figlet": "^1.9.4",
44
+ "fs-extra": "^11.1.1",
45
+ "gradient-string": "^2.0.2",
46
+ "js-yaml": "^4.1.0",
47
+ "oh-my-logo": "^0.4.0",
48
+ "prompts": "^2.4.2"
49
+ },
50
+ "engines": {
51
+ "node": ">=14.0.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/js-yaml": "^4.0.9",
55
+ "@types/node": "^24.10.2",
56
+ "glob": "^13.0.0",
57
+ "markdownlint": "^0.40.0",
58
+ "markdownlint-cli": "^0.46.0",
59
+ "remark-parse": "^11.0.0",
60
+ "typescript": "^5.9.3",
61
+ "unified": "^11.0.5",
62
+ "unist-util-visit": "^5.0.0",
63
+ "vitest": "^4.0.15"
64
+ }
65
+ }