roadmapsmith 0.9.14 → 0.9.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roadmapsmith",
3
- "version": "0.9.14",
3
+ "version": "0.9.15",
4
4
  "description": "Evidence-backed ROADMAP.md generator and sync tool for AI coding agents.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/config.js CHANGED
@@ -24,6 +24,12 @@ const DEFAULT_CONFIG = {
24
24
  steps: [],
25
25
  phases: []
26
26
  },
27
+ zeroMode: {
28
+ problemStatement: '',
29
+ preferredStack: '',
30
+ constraints: [],
31
+ doneCriteria: []
32
+ },
27
33
  validation: {
28
34
  minimumConfidence: 'low'
29
35
  },
@@ -77,6 +83,16 @@ function mergeConfig(userConfig) {
77
83
  ? userConfig.product.phases
78
84
  : DEFAULT_CONFIG.product.phases
79
85
  },
86
+ zeroMode: {
87
+ ...DEFAULT_CONFIG.zeroMode,
88
+ ...((userConfig && userConfig.zeroMode) || {}),
89
+ constraints: (userConfig && userConfig.zeroMode && Array.isArray(userConfig.zeroMode.constraints))
90
+ ? userConfig.zeroMode.constraints
91
+ : DEFAULT_CONFIG.zeroMode.constraints,
92
+ doneCriteria: (userConfig && userConfig.zeroMode && Array.isArray(userConfig.zeroMode.doneCriteria))
93
+ ? userConfig.zeroMode.doneCriteria
94
+ : DEFAULT_CONFIG.zeroMode.doneCriteria
95
+ },
80
96
  validation: {
81
97
  ...DEFAULT_CONFIG.validation,
82
98
  ...((userConfig && userConfig.validation) || {})
@@ -84,11 +100,15 @@ function mergeConfig(userConfig) {
84
100
  };
85
101
  }
86
102
 
87
- function loadConfig(options = {}) {
103
+ function resolveConfigPath(options = {}) {
88
104
  const projectRoot = path.resolve(options.projectRoot || process.cwd());
89
- const resolvedConfigPath = options.configPath
105
+ return options.configPath
90
106
  ? path.resolve(projectRoot, String(options.configPath))
91
107
  : path.resolve(projectRoot, 'roadmap-skill.config.json');
108
+ }
109
+
110
+ function loadConfig(options = {}) {
111
+ const resolvedConfigPath = resolveConfigPath(options);
92
112
 
93
113
  const content = readTextIfExists(resolvedConfigPath);
94
114
  let userConfig = {};
@@ -114,6 +134,15 @@ function loadConfig(options = {}) {
114
134
  return merged;
115
135
  }
116
136
 
137
+ function readUserConfig(options = {}) {
138
+ const resolvedConfigPath = resolveConfigPath(options);
139
+ const content = readTextIfExists(resolvedConfigPath);
140
+ if (!content) {
141
+ return {};
142
+ }
143
+ return safeParseJson(content, resolvedConfigPath);
144
+ }
145
+
117
146
  function resolveRoadmapFile(projectRoot, config, overridePath) {
118
147
  if (overridePath) {
119
148
  return path.resolve(projectRoot, overridePath);
@@ -214,6 +243,8 @@ module.exports = {
214
243
  collectPluginContributions,
215
244
  loadConfig,
216
245
  loadPlugins,
246
+ readUserConfig,
217
247
  resolveAgentsFile,
248
+ resolveConfigPath,
218
249
  resolveRoadmapFile
219
250
  };
@@ -490,11 +490,12 @@ function createModel(scan, tasks, config, customSections, checkedById) {
490
490
  }
491
491
 
492
492
  const productConfig = config.product || {};
493
+ const zeroModeConfig = config.zeroMode || {};
493
494
  const inferredName = inferProjectName(scan.projectRoot || process.cwd());
494
495
  const product = {
495
496
  name: productConfig.name || inferredName,
496
497
  northStar: productConfig.northStar || '',
497
- positioning: productConfig.positioning || '',
498
+ positioning: productConfig.positioning || (zeroModeConfig.problemStatement ? `Core problem: ${zeroModeConfig.problemStatement}` : ''),
498
499
  primaryUser: productConfig.primaryUser || '',
499
500
  targetOutcome: productConfig.targetOutcome || ''
500
501
  };
@@ -510,9 +511,16 @@ function createModel(scan, tasks, config, customSections, checkedById) {
510
511
  'Do not hide validation failures from roadmap consumers'
511
512
  ];
512
513
 
513
- const risks = (productConfig.risks && productConfig.risks.length > 0) ? productConfig.risks : defaultRisks;
514
+ const derivedConstraintRisks = Array.isArray(zeroModeConfig.constraints)
515
+ ? zeroModeConfig.constraints.map((constraint) => `Constraint: ${constraint}`)
516
+ : [];
517
+ const risks = (productConfig.risks && productConfig.risks.length > 0)
518
+ ? productConfig.risks
519
+ : (derivedConstraintRisks.length > 0 ? derivedConstraintRisks : defaultRisks);
514
520
  const antiGoals = (productConfig.antiGoals && productConfig.antiGoals.length > 0) ? productConfig.antiGoals : defaultAntiGoals;
515
- const successCriteria = productConfig.successCriteria || [];
521
+ const successCriteria = (productConfig.successCriteria && productConfig.successCriteria.length > 0)
522
+ ? productConfig.successCriteria
523
+ : (Array.isArray(zeroModeConfig.doneCriteria) ? zeroModeConfig.doneCriteria : []);
516
524
 
517
525
  const northStar = productConfig.northStar
518
526
  || 'Ship validated, high-impact increments with deterministic delivery and transparent completion evidence.';
@@ -555,6 +563,7 @@ function normalizeCandidate(candidate) {
555
563
  function generateRoadmapDocument(options) {
556
564
  const projectRoot = options.projectRoot;
557
565
  const config = options.config;
566
+ const zeroModeConfig = config.zeroMode || {};
558
567
  const plugins = options.plugins || [];
559
568
  const existingContent = options.existingContent || '';
560
569
 
@@ -581,6 +590,24 @@ function generateRoadmapDocument(options) {
581
590
  items: section.items || []
582
591
  }));
583
592
 
593
+ const zeroModeBriefItems = [];
594
+ if (zeroModeConfig.problemStatement) {
595
+ zeroModeBriefItems.push(`- **Problem statement:** ${zeroModeConfig.problemStatement}`);
596
+ }
597
+ if (zeroModeConfig.preferredStack) {
598
+ zeroModeBriefItems.push(`- **Preferred stack:** ${zeroModeConfig.preferredStack}`);
599
+ }
600
+ if (Array.isArray(zeroModeConfig.constraints) && zeroModeConfig.constraints.length > 0) {
601
+ zeroModeBriefItems.push(`- **Constraints:** ${zeroModeConfig.constraints.join('; ')}`);
602
+ }
603
+ if (Array.isArray(zeroModeConfig.doneCriteria) && zeroModeConfig.doneCriteria.length > 0) {
604
+ zeroModeBriefItems.push(`- **Done means:** ${zeroModeConfig.doneCriteria.join('; ')}`);
605
+ }
606
+ const generatedZeroModeSection = zeroModeBriefItems.length > 0 ? [{
607
+ title: 'Zero Mode Brief',
608
+ items: zeroModeBriefItems
609
+ }] : [];
610
+
584
611
  const configSections = (config.customSections || []).map((section) => ({
585
612
  title: section.title,
586
613
  items: section.items || []
@@ -601,7 +628,7 @@ function generateRoadmapDocument(options) {
601
628
  const baseCandidates = buildDefaultCandidates(scan, config);
602
629
  const matcherCandidates = applyTaskMatchers(scan, config);
603
630
  const merged = mergeWithExisting([...baseCandidates, ...matcherCandidates, ...pluginTaskCandidates], existingPhaseTasks);
604
- const model = createModel(scan, merged, config, [profileSection, ...configSections, ...pluginSections], existingCheckedById);
631
+ const model = createModel(scan, merged, config, [profileSection, ...generatedZeroModeSection, ...configSections, ...pluginSections], existingCheckedById);
605
632
  const profile = config.roadmapProfile || 'compact';
606
633
  const managedBody = renderBody(model, profile);
607
634