musubi-sdd 2.2.0 → 3.0.0

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.
@@ -0,0 +1,508 @@
1
+ /**
2
+ * Intermediate Representation (IR) Types for Cross-Format Conversion
3
+ *
4
+ * Enables bidirectional conversion between MUSUBI and Spec Kit formats
5
+ * with minimal information loss.
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ /**
11
+ * EARS Requirement Patterns
12
+ * @typedef {'ubiquitous' | 'event-driven' | 'state-driven' | 'optional' | 'complex'} EARSPattern
13
+ */
14
+
15
+ /**
16
+ * Priority levels
17
+ * @typedef {'P0' | 'P1' | 'P2' | 'P3'} Priority
18
+ */
19
+
20
+ /**
21
+ * Feature status
22
+ * @typedef {'draft' | 'in-progress' | 'completed'} FeatureStatus
23
+ */
24
+
25
+ /**
26
+ * Source format
27
+ * @typedef {'musubi' | 'speckit'} SourceFormat
28
+ */
29
+
30
+ /**
31
+ * Project Metadata
32
+ * @typedef {Object} ProjectMetadata
33
+ * @property {string} name - Project name
34
+ * @property {string} version - Project version
35
+ * @property {SourceFormat} sourceFormat - Original format
36
+ * @property {string} sourceVersion - Original format version
37
+ * @property {Date} convertedAt - Conversion timestamp
38
+ * @property {Object} preservedFields - Format-specific fields preserved
39
+ */
40
+
41
+ /**
42
+ * Constitution Article (MUSUBI 9 Articles)
43
+ * @typedef {Object} ArticleIR
44
+ * @property {number} number - Article number (1-9)
45
+ * @property {string} name - Article name
46
+ * @property {string} description - Article description
47
+ * @property {string[]} rules - Article rules
48
+ * @property {string} [mappedFrom] - Original section if converted
49
+ */
50
+
51
+ /**
52
+ * Spec Kit Principle
53
+ * @typedef {Object} PrincipleIR
54
+ * @property {string} name - Principle name
55
+ * @property {string} description - Principle description
56
+ * @property {number} [mappedToArticle] - Mapped MUSUBI article number
57
+ */
58
+
59
+ /**
60
+ * Governance information
61
+ * @typedef {Object} GovernanceIR
62
+ * @property {string} version - Governance version
63
+ * @property {Date} [ratified] - Ratification date
64
+ * @property {Date} [lastAmended] - Last amendment date
65
+ * @property {string[]} rules - Governance rules
66
+ */
67
+
68
+ /**
69
+ * Constitution IR
70
+ * @typedef {Object} ConstitutionIR
71
+ * @property {ArticleIR[]} articles - MUSUBI articles
72
+ * @property {PrincipleIR[]} corePrinciples - Spec Kit principles
73
+ * @property {GovernanceIR} governance - Governance info
74
+ * @property {string} [rawContent] - Original raw content
75
+ */
76
+
77
+ /**
78
+ * Acceptance Criterion
79
+ * @typedef {Object} AcceptanceCriterionIR
80
+ * @property {string} id - Criterion ID
81
+ * @property {string} description - Criterion description
82
+ * @property {boolean} testable - Whether testable
83
+ */
84
+
85
+ /**
86
+ * User Scenario (Spec Kit style)
87
+ * @typedef {Object} UserScenarioIR
88
+ * @property {string} id - Scenario ID
89
+ * @property {string} title - Scenario title
90
+ * @property {string} actor - Who performs the action
91
+ * @property {string} action - What action is performed
92
+ * @property {string} benefit - Expected benefit
93
+ * @property {Priority} priority - Priority level
94
+ * @property {AcceptanceCriterionIR[]} acceptanceCriteria - Acceptance criteria
95
+ */
96
+
97
+ /**
98
+ * Requirement (EARS format)
99
+ * @typedef {Object} RequirementIR
100
+ * @property {string} id - Requirement ID (e.g., "REQ-001")
101
+ * @property {string} title - Requirement title
102
+ * @property {EARSPattern} pattern - EARS pattern type
103
+ * @property {Priority} priority - Priority level
104
+ * @property {string} [trigger] - WHEN clause
105
+ * @property {string} [condition] - WHILE/WHERE clause
106
+ * @property {string} action - SHALL clause
107
+ * @property {string} statement - Full EARS statement
108
+ * @property {AcceptanceCriterionIR[]} acceptanceCriteria - Acceptance criteria
109
+ * @property {string} [traceability] - Traceability info
110
+ * @property {string} [mappedFromUserStory] - Original user story if converted
111
+ */
112
+
113
+ /**
114
+ * Specification IR
115
+ * @typedef {Object} SpecificationIR
116
+ * @property {string} title - Specification title
117
+ * @property {string} description - Specification description
118
+ * @property {UserScenarioIR[]} userScenarios - User scenarios
119
+ * @property {RequirementIR[]} requirements - EARS requirements
120
+ * @property {string[]} successCriteria - Success criteria
121
+ * @property {string} [rawContent] - Original raw content
122
+ */
123
+
124
+ /**
125
+ * Technical Context
126
+ * @typedef {Object} TechnicalContextIR
127
+ * @property {string} language - Primary language
128
+ * @property {string} version - Language version
129
+ * @property {string} framework - Primary framework
130
+ * @property {string[]} dependencies - Dependencies
131
+ * @property {string} [storage] - Storage solution
132
+ * @property {string} testing - Testing framework
133
+ * @property {string} targetPlatform - Target platform
134
+ * @property {string} [performanceGoals] - Performance goals
135
+ * @property {string} [constraints] - Constraints
136
+ * @property {string} [scale] - Scale requirements
137
+ */
138
+
139
+ /**
140
+ * Constitution Check
141
+ * @typedef {Object} ConstitutionCheckIR
142
+ * @property {string} principle - Principle name
143
+ * @property {'pass' | 'fail' | 'needs-justification'} status - Check status
144
+ * @property {string} [notes] - Additional notes
145
+ */
146
+
147
+ /**
148
+ * Directory structure
149
+ * @typedef {Object} DirectoryIR
150
+ * @property {string} path - Directory path
151
+ * @property {string} purpose - Directory purpose
152
+ * @property {DirectoryIR[]} [children] - Child directories
153
+ */
154
+
155
+ /**
156
+ * Project Structure
157
+ * @typedef {Object} ProjectStructureIR
158
+ * @property {'single' | 'web' | 'mobile' | 'monorepo'} type - Project type
159
+ * @property {DirectoryIR[]} directories - Directory structure
160
+ */
161
+
162
+ /**
163
+ * Task Reference
164
+ * @typedef {Object} TaskReferenceIR
165
+ * @property {string} taskId - Task ID
166
+ * @property {number} order - Execution order
167
+ */
168
+
169
+ /**
170
+ * Phase
171
+ * @typedef {Object} PhaseIR
172
+ * @property {number} number - Phase number
173
+ * @property {string} name - Phase name
174
+ * @property {string} purpose - Phase purpose
175
+ * @property {string[]} [prerequisites] - Prerequisites
176
+ * @property {string[]} outputs - Phase outputs
177
+ * @property {TaskReferenceIR[]} tasks - Tasks in this phase
178
+ */
179
+
180
+ /**
181
+ * Plan IR
182
+ * @typedef {Object} PlanIR
183
+ * @property {string} summary - Plan summary
184
+ * @property {TechnicalContextIR} technicalContext - Technical context
185
+ * @property {ConstitutionCheckIR[]} constitutionCheck - Constitution checks
186
+ * @property {ProjectStructureIR} projectStructure - Project structure
187
+ * @property {PhaseIR[]} phases - Implementation phases
188
+ * @property {string} [rawContent] - Original raw content
189
+ */
190
+
191
+ /**
192
+ * Task IR
193
+ * @typedef {Object} TaskIR
194
+ * @property {string} id - Task ID (e.g., "T001")
195
+ * @property {string} description - Task description
196
+ * @property {number} phase - Phase number
197
+ * @property {string} [userStory] - Related user story
198
+ * @property {boolean} parallel - Can run in parallel
199
+ * @property {string} [filePath] - Related file path
200
+ * @property {boolean} completed - Completion status
201
+ * @property {string[]} [dependencies] - Task dependencies
202
+ */
203
+
204
+ /**
205
+ * Decision
206
+ * @typedef {Object} DecisionIR
207
+ * @property {string} topic - Decision topic
208
+ * @property {string} decision - Decision made
209
+ * @property {string} rationale - Decision rationale
210
+ */
211
+
212
+ /**
213
+ * Alternative
214
+ * @typedef {Object} AlternativeIR
215
+ * @property {string} name - Alternative name
216
+ * @property {string[]} pros - Pros
217
+ * @property {string[]} cons - Cons
218
+ * @property {boolean} rejected - Whether rejected
219
+ * @property {string} [reason] - Rejection reason
220
+ */
221
+
222
+ /**
223
+ * Research IR
224
+ * @typedef {Object} ResearchIR
225
+ * @property {DecisionIR[]} decisions - Decisions made
226
+ * @property {AlternativeIR[]} alternatives - Alternatives considered
227
+ * @property {string} [rawContent] - Original raw content
228
+ */
229
+
230
+ /**
231
+ * Entity
232
+ * @typedef {Object} EntityIR
233
+ * @property {string} name - Entity name
234
+ * @property {string} description - Entity description
235
+ * @property {Object[]} fields - Entity fields
236
+ */
237
+
238
+ /**
239
+ * Relationship
240
+ * @typedef {Object} RelationshipIR
241
+ * @property {string} from - Source entity
242
+ * @property {string} to - Target entity
243
+ * @property {string} type - Relationship type
244
+ * @property {string} [cardinality] - Cardinality
245
+ */
246
+
247
+ /**
248
+ * Data Model IR
249
+ * @typedef {Object} DataModelIR
250
+ * @property {EntityIR[]} entities - Entities
251
+ * @property {RelationshipIR[]} relationships - Relationships
252
+ * @property {string} [rawContent] - Original raw content
253
+ */
254
+
255
+ /**
256
+ * Contract IR
257
+ * @typedef {Object} ContractIR
258
+ * @property {string} type - Contract type (REST, GraphQL, etc.)
259
+ * @property {string} name - Contract name
260
+ * @property {Object} definition - Contract definition
261
+ * @property {string} [rawContent] - Original raw content
262
+ */
263
+
264
+ /**
265
+ * Quickstart IR
266
+ * @typedef {Object} QuickstartIR
267
+ * @property {Object[]} steps - Setup steps
268
+ * @property {string} [rawContent] - Original raw content
269
+ */
270
+
271
+ /**
272
+ * Feature IR
273
+ * @typedef {Object} FeatureIR
274
+ * @property {string} id - Feature ID (e.g., "001-photo-albums")
275
+ * @property {string} name - Feature name
276
+ * @property {string} [branch] - Feature branch
277
+ * @property {FeatureStatus} status - Feature status
278
+ * @property {Date} createdAt - Creation date
279
+ * @property {SpecificationIR} specification - Specification
280
+ * @property {PlanIR} [plan] - Implementation plan
281
+ * @property {TaskIR[]} [tasks] - Tasks
282
+ * @property {ResearchIR} [research] - Research
283
+ * @property {DataModelIR} [dataModel] - Data model
284
+ * @property {ContractIR[]} [contracts] - API contracts
285
+ * @property {QuickstartIR} [quickstart] - Quickstart guide
286
+ */
287
+
288
+ /**
289
+ * Template IR
290
+ * @typedef {Object} TemplateIR
291
+ * @property {string} name - Template name
292
+ * @property {string} type - Template type
293
+ * @property {string} content - Template content
294
+ */
295
+
296
+ /**
297
+ * Memory IR
298
+ * @typedef {Object} MemoryIR
299
+ * @property {string} category - Memory category
300
+ * @property {Object[]} entries - Memory entries
301
+ */
302
+
303
+ /**
304
+ * Project IR - Root structure
305
+ * @typedef {Object} ProjectIR
306
+ * @property {ProjectMetadata} metadata - Project metadata
307
+ * @property {ConstitutionIR} constitution - Constitution
308
+ * @property {FeatureIR[]} features - Features
309
+ * @property {TemplateIR[]} templates - Templates
310
+ * @property {MemoryIR[]} memories - Memories
311
+ */
312
+
313
+ /**
314
+ * Create empty Project IR
315
+ * @returns {ProjectIR}
316
+ */
317
+ function createEmptyProjectIR() {
318
+ return {
319
+ metadata: {
320
+ name: '',
321
+ version: '1.0.0',
322
+ sourceFormat: 'musubi',
323
+ sourceVersion: '2.2.0',
324
+ convertedAt: new Date(),
325
+ preservedFields: {},
326
+ },
327
+ constitution: {
328
+ articles: [],
329
+ corePrinciples: [],
330
+ governance: {
331
+ version: '1.0',
332
+ rules: [],
333
+ },
334
+ },
335
+ features: [],
336
+ templates: [],
337
+ memories: [],
338
+ };
339
+ }
340
+
341
+ /**
342
+ * Create empty Feature IR
343
+ * @param {string} id - Feature ID
344
+ * @param {string} name - Feature name
345
+ * @returns {FeatureIR}
346
+ */
347
+ function createEmptyFeatureIR(id, name) {
348
+ return {
349
+ id,
350
+ name,
351
+ status: 'draft',
352
+ createdAt: new Date(),
353
+ specification: {
354
+ title: name,
355
+ description: '',
356
+ userScenarios: [],
357
+ requirements: [],
358
+ successCriteria: [],
359
+ },
360
+ };
361
+ }
362
+
363
+ /**
364
+ * Create Requirement IR from EARS statement
365
+ * @param {string} id - Requirement ID
366
+ * @param {string} statement - EARS statement
367
+ * @returns {RequirementIR}
368
+ */
369
+ function createRequirementFromEARS(id, statement) {
370
+ const pattern = detectEARSPattern(statement);
371
+ const parsed = parseEARSStatement(statement, pattern);
372
+
373
+ return {
374
+ id,
375
+ title: '',
376
+ pattern,
377
+ priority: 'P1',
378
+ trigger: parsed.trigger,
379
+ condition: parsed.condition,
380
+ action: parsed.action,
381
+ statement,
382
+ acceptanceCriteria: [],
383
+ };
384
+ }
385
+
386
+ /**
387
+ * Detect EARS pattern from statement
388
+ * @param {string} statement - EARS statement
389
+ * @returns {EARSPattern}
390
+ */
391
+ function detectEARSPattern(statement) {
392
+ const upper = statement.toUpperCase();
393
+
394
+ if (upper.includes('WHILE') && upper.includes('WHEN')) {
395
+ return 'complex';
396
+ }
397
+ if (upper.includes('WHEN')) {
398
+ return 'event-driven';
399
+ }
400
+ if (upper.includes('WHILE')) {
401
+ return 'state-driven';
402
+ }
403
+ if (upper.includes('WHERE')) {
404
+ return 'optional';
405
+ }
406
+ return 'ubiquitous';
407
+ }
408
+
409
+ /**
410
+ * Parse EARS statement into components
411
+ * @param {string} statement - EARS statement
412
+ * @param {EARSPattern} pattern - EARS pattern
413
+ * @returns {{trigger?: string, condition?: string, action: string}}
414
+ */
415
+ function parseEARSStatement(statement, pattern) {
416
+ const result = { action: '' };
417
+
418
+ // Extract SHALL clause
419
+ const shallMatch = statement.match(/SHALL\s+(.+?)(?:\.|$)/i);
420
+ if (shallMatch) {
421
+ result.action = shallMatch[1].trim();
422
+ }
423
+
424
+ // Extract WHEN clause
425
+ const whenMatch = statement.match(/WHEN\s+(.+?),/i);
426
+ if (whenMatch) {
427
+ result.trigger = whenMatch[1].trim();
428
+ }
429
+
430
+ // Extract WHILE clause
431
+ const whileMatch = statement.match(/WHILE\s+(.+?),/i);
432
+ if (whileMatch) {
433
+ result.condition = whileMatch[1].trim();
434
+ }
435
+
436
+ // Extract WHERE clause
437
+ const whereMatch = statement.match(/WHERE\s+(.+?),/i);
438
+ if (whereMatch) {
439
+ result.condition = whereMatch[1].trim();
440
+ }
441
+
442
+ return result;
443
+ }
444
+
445
+ /**
446
+ * Convert User Story to EARS Requirement
447
+ * @param {UserScenarioIR} userScenario - User scenario
448
+ * @param {string} reqId - Requirement ID
449
+ * @returns {RequirementIR}
450
+ */
451
+ function userScenarioToRequirement(userScenario, reqId) {
452
+ // Convert "As a [actor], I want [action] so that [benefit]"
453
+ // to "WHEN the [actor] [action], the system SHALL [provide benefit]"
454
+ const statement = `WHEN the ${userScenario.actor} ${userScenario.action}, the system SHALL ${userScenario.benefit}.`;
455
+
456
+ return {
457
+ id: reqId,
458
+ title: userScenario.title,
459
+ pattern: 'event-driven',
460
+ priority: userScenario.priority,
461
+ trigger: `the ${userScenario.actor} ${userScenario.action}`,
462
+ action: userScenario.benefit,
463
+ statement,
464
+ acceptanceCriteria: userScenario.acceptanceCriteria,
465
+ mappedFromUserStory: userScenario.id,
466
+ };
467
+ }
468
+
469
+ /**
470
+ * Convert EARS Requirement to User Story
471
+ * @param {RequirementIR} requirement - Requirement
472
+ * @param {string} storyId - Story ID
473
+ * @returns {UserScenarioIR}
474
+ */
475
+ function requirementToUserScenario(requirement, storyId) {
476
+ // Convert EARS to User Story format
477
+ let actor = 'user';
478
+ let action = requirement.trigger || 'performs an action';
479
+ let benefit = requirement.action;
480
+
481
+ // Try to extract actor from trigger
482
+ if (requirement.trigger) {
483
+ const actorMatch = requirement.trigger.match(/the\s+(\w+)/i);
484
+ if (actorMatch) {
485
+ actor = actorMatch[1];
486
+ }
487
+ }
488
+
489
+ return {
490
+ id: storyId,
491
+ title: requirement.title || `Story for ${requirement.id}`,
492
+ actor,
493
+ action,
494
+ benefit,
495
+ priority: requirement.priority,
496
+ acceptanceCriteria: requirement.acceptanceCriteria,
497
+ };
498
+ }
499
+
500
+ module.exports = {
501
+ createEmptyProjectIR,
502
+ createEmptyFeatureIR,
503
+ createRequirementFromEARS,
504
+ detectEARSPattern,
505
+ parseEARSStatement,
506
+ userScenarioToRequirement,
507
+ requirementToUserScenario,
508
+ };