jumpstart-mode 1.1.11 → 1.1.13
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/.github/agents/jumpstart-adversary.agent.md +2 -1
- package/.github/agents/jumpstart-architect.agent.md +6 -7
- package/.github/agents/jumpstart-challenger.agent.md +2 -1
- package/.github/agents/jumpstart-developer.agent.md +1 -1
- package/.github/agents/jumpstart-devops.agent.md +2 -2
- package/.github/agents/jumpstart-diagram-verifier.agent.md +2 -1
- package/.github/agents/jumpstart-maintenance.agent.md +1 -0
- package/.github/agents/jumpstart-performance.agent.md +1 -0
- package/.github/agents/jumpstart-pm.agent.md +1 -1
- package/.github/agents/jumpstart-refactor.agent.md +1 -0
- package/.github/agents/jumpstart-requirements-extractor.agent.md +1 -0
- package/.github/agents/jumpstart-researcher.agent.md +1 -0
- package/.github/agents/jumpstart-retrospective.agent.md +1 -0
- package/.github/agents/jumpstart-reviewer.agent.md +2 -0
- package/.github/agents/jumpstart-scout.agent.md +1 -1
- package/.github/agents/jumpstart-scrum-master.agent.md +1 -0
- package/.github/agents/jumpstart-security.agent.md +2 -1
- package/.github/agents/jumpstart-tech-writer.agent.md +1 -0
- package/.github/agents/jumpstart-uiux-designer.agent.md +66 -0
- package/.github/workflows/quality.yml +19 -2
- package/.jumpstart/agents/analyst.md +38 -0
- package/.jumpstart/agents/architect.md +39 -1
- package/.jumpstart/agents/challenger.md +38 -0
- package/.jumpstart/agents/developer.md +41 -0
- package/.jumpstart/agents/pm.md +38 -0
- package/.jumpstart/agents/scout.md +33 -0
- package/.jumpstart/agents/ux-designer.md +29 -9
- package/.jumpstart/commands/commands.md +6 -5
- package/.jumpstart/config.yaml +25 -1
- package/.jumpstart/roadmap.md +1 -1
- package/.jumpstart/schemas/timeline.schema.json +1 -0
- package/.jumpstart/skills/README.md +1 -0
- package/.jumpstart/skills/quality-gates/SKILL.md +126 -0
- package/.jumpstart/skills/skill-creator/SKILL.md +485 -357
- package/.jumpstart/skills/skill-creator/agents/analyzer.md +274 -0
- package/.jumpstart/skills/skill-creator/agents/comparator.md +202 -0
- package/.jumpstart/skills/skill-creator/agents/grader.md +223 -0
- package/.jumpstart/skills/skill-creator/assets/eval_review.html +146 -0
- package/.jumpstart/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/.jumpstart/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/.jumpstart/skills/skill-creator/references/schemas.md +430 -0
- package/.jumpstart/skills/skill-creator/scripts/__init__.py +0 -0
- package/.jumpstart/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/.jumpstart/skills/skill-creator/scripts/generate_report.py +326 -0
- package/.jumpstart/skills/skill-creator/scripts/improve_description.py +247 -0
- package/.jumpstart/skills/skill-creator/scripts/package_skill.py +136 -110
- package/.jumpstart/skills/skill-creator/scripts/run_eval.py +310 -0
- package/.jumpstart/skills/skill-creator/scripts/run_loop.py +328 -0
- package/.jumpstart/skills/skill-creator/scripts/utils.py +47 -0
- package/.jumpstart/skills/ui-ux-pro-max/SKILL.md +266 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.jumpstart/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.jumpstart/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.jumpstart/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.jumpstart/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.jumpstart/state/timeline.json +659 -0
- package/.jumpstart/templates/model-map.md +1 -1
- package/.jumpstart/templates/ux-design.md +3 -3
- package/.jumpstart/usage-log.json +74 -3
- package/AGENTS.md +1 -1
- package/README.md +64 -3
- package/bin/cli.js +3217 -1
- package/bin/headless-runner.js +62 -2
- package/bin/lib/agent-checkpoint.js +168 -0
- package/bin/lib/ai-evaluation.js +104 -0
- package/bin/lib/ai-intake.js +152 -0
- package/bin/lib/ambiguity-heatmap.js +152 -0
- package/bin/lib/artifact-comparison.js +104 -0
- package/bin/lib/ast-edit-engine.js +157 -0
- package/bin/lib/backlog-sync.js +338 -0
- package/bin/lib/bcdr-planning.js +158 -0
- package/bin/lib/bidirectional-trace.js +199 -0
- package/bin/lib/branch-workflow.js +266 -0
- package/bin/lib/cab-output.js +119 -0
- package/bin/lib/chat-integration.js +122 -0
- package/bin/lib/ci-cd-integration.js +208 -0
- package/bin/lib/codebase-retrieval.js +125 -0
- package/bin/lib/collaboration.js +168 -0
- package/bin/lib/compliance-packs.js +213 -0
- package/bin/lib/context-chunker.js +128 -0
- package/bin/lib/context-onboarding.js +122 -0
- package/bin/lib/contract-first.js +124 -0
- package/bin/lib/cost-router.js +148 -0
- package/bin/lib/credential-boundary.js +155 -0
- package/bin/lib/data-classification.js +180 -0
- package/bin/lib/data-contracts.js +129 -0
- package/bin/lib/db-evolution.js +158 -0
- package/bin/lib/decision-conflicts.js +299 -0
- package/bin/lib/delivery-confidence.js +361 -0
- package/bin/lib/dependency-upgrade.js +153 -0
- package/bin/lib/design-system.js +133 -0
- package/bin/lib/deterministic-artifacts.js +151 -0
- package/bin/lib/diagram-studio.js +115 -0
- package/bin/lib/domain-ontology.js +140 -0
- package/bin/lib/ea-review-packet.js +151 -0
- package/bin/lib/enterprise-search.js +123 -0
- package/bin/lib/enterprise-templates.js +140 -0
- package/bin/lib/environment-promotion.js +220 -0
- package/bin/lib/estimation-studio.js +130 -0
- package/bin/lib/event-modeling.js +133 -0
- package/bin/lib/evidence-collector.js +179 -0
- package/bin/lib/finops-planner.js +182 -0
- package/bin/lib/fitness-functions.js +279 -0
- package/bin/lib/focus.js +448 -0
- package/bin/lib/governance-dashboard.js +165 -0
- package/bin/lib/guided-handoff.js +120 -0
- package/bin/lib/impact-analysis.js +190 -0
- package/bin/lib/incident-feedback.js +157 -0
- package/bin/lib/integrate.js +1 -1
- package/bin/lib/knowledge-graph.js +122 -0
- package/bin/lib/legacy-modernizer.js +160 -0
- package/bin/lib/migration-planner.js +144 -0
- package/bin/lib/model-governance.js +185 -0
- package/bin/lib/model-router.js +144 -0
- package/bin/lib/multi-repo.js +272 -0
- package/bin/lib/next-phase.js +53 -8
- package/bin/lib/ops-ownership.js +152 -0
- package/bin/lib/parallel-agents.js +257 -0
- package/bin/lib/pattern-library.js +115 -0
- package/bin/lib/persona-packs.js +99 -0
- package/bin/lib/plan-executor.js +366 -0
- package/bin/lib/platform-engineering.js +119 -0
- package/bin/lib/playback-summaries.js +126 -0
- package/bin/lib/policy-engine.js +240 -0
- package/bin/lib/portfolio-reporting.js +357 -0
- package/bin/lib/pr-package.js +197 -0
- package/bin/lib/project-memory.js +235 -0
- package/bin/lib/prompt-governance.js +130 -0
- package/bin/lib/promptless-mode.js +128 -0
- package/bin/lib/quality-graph.js +193 -0
- package/bin/lib/raci-matrix.js +188 -0
- package/bin/lib/refactor-planner.js +167 -0
- package/bin/lib/reference-architectures.js +304 -0
- package/bin/lib/release-readiness.js +171 -0
- package/bin/lib/repo-graph.js +262 -0
- package/bin/lib/requirements-baseline.js +358 -0
- package/bin/lib/risk-register.js +211 -0
- package/bin/lib/role-approval.js +249 -0
- package/bin/lib/role-views.js +142 -0
- package/bin/lib/root-cause-analysis.js +132 -0
- package/bin/lib/runtime-debugger.js +154 -0
- package/bin/lib/safe-rename.js +135 -0
- package/bin/lib/secret-scanner.js +313 -0
- package/bin/lib/semantic-diff.js +335 -0
- package/bin/lib/sla-slo.js +210 -0
- package/bin/lib/smoke-tester.js +344 -0
- package/bin/lib/spec-comments.js +147 -0
- package/bin/lib/spec-maturity.js +287 -0
- package/bin/lib/sre-integration.js +154 -0
- package/bin/lib/structured-elicitation.js +174 -0
- package/bin/lib/telemetry-feedback.js +118 -0
- package/bin/lib/test-generator.js +146 -0
- package/bin/lib/timeline.js +2 -1
- package/bin/lib/tool-bridge.js +159 -0
- package/bin/lib/tool-guardrails.js +139 -0
- package/bin/lib/tool-schemas.js +281 -3
- package/bin/lib/transcript-ingestion.js +150 -0
- package/bin/lib/type-checker.js +261 -0
- package/bin/lib/uat-coverage.js +411 -0
- package/bin/lib/vendor-risk.js +173 -0
- package/bin/lib/waiver-workflow.js +174 -0
- package/bin/lib/web-dashboard.js +126 -0
- package/bin/lib/workshop-mode.js +165 -0
- package/bin/lib/workstream-ownership.js +104 -0
- package/package.json +1 -1
- package/.github/agents/jumpstart-ux-designer.agent.md +0 -45
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spec-maturity.js — Spec Maturity Model
|
|
3
|
+
*
|
|
4
|
+
* Score artifacts from draft to production-ready with explicit gaps
|
|
5
|
+
* for enterprise delivery.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node bin/lib/spec-maturity.js assess|report [options]
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const MATURITY_LEVELS = [
|
|
17
|
+
{ level: 1, name: 'Draft', min_score: 0, description: 'Initial capture, may contain placeholders and gaps' },
|
|
18
|
+
{ level: 2, name: 'Reviewed', min_score: 30, description: 'Peer-reviewed, major gaps addressed' },
|
|
19
|
+
{ level: 3, name: 'Approved', min_score: 55, description: 'Stakeholder-approved, phase gate passed' },
|
|
20
|
+
{ level: 4, name: 'Implementation-Ready', min_score: 75, description: 'Detailed enough for developers to implement' },
|
|
21
|
+
{ level: 5, name: 'Production-Ready', min_score: 90, description: 'Complete, validated, enterprise-ready' }
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const MATURITY_CRITERIA = {
|
|
25
|
+
structure: {
|
|
26
|
+
weight: 0.15,
|
|
27
|
+
checks: [
|
|
28
|
+
{ id: 'has_frontmatter', description: 'YAML frontmatter present' },
|
|
29
|
+
{ id: 'has_toc', description: 'Table of contents present' },
|
|
30
|
+
{ id: 'has_sections', description: 'Proper heading hierarchy' },
|
|
31
|
+
{ id: 'no_empty_sections', description: 'No empty sections' }
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
completeness: {
|
|
35
|
+
weight: 0.25,
|
|
36
|
+
checks: [
|
|
37
|
+
{ id: 'no_placeholders', description: 'No TODO/TBD/placeholder markers' },
|
|
38
|
+
{ id: 'no_clarifications', description: 'No [NEEDS CLARIFICATION] tags' },
|
|
39
|
+
{ id: 'sufficient_length', description: 'Sufficient content (>1000 chars)' },
|
|
40
|
+
{ id: 'has_detail', description: 'Detailed descriptions (avg paragraph >50 words)' }
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
traceability: {
|
|
44
|
+
weight: 0.15,
|
|
45
|
+
checks: [
|
|
46
|
+
{ id: 'has_requirement_ids', description: 'Contains requirement/story IDs' },
|
|
47
|
+
{ id: 'has_cross_refs', description: 'Cross-references to other artifacts' },
|
|
48
|
+
{ id: 'has_version', description: 'Version information present' }
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
quality: {
|
|
52
|
+
weight: 0.20,
|
|
53
|
+
checks: [
|
|
54
|
+
{ id: 'has_acceptance_criteria', description: 'Acceptance criteria defined' },
|
|
55
|
+
{ id: 'has_diagrams', description: 'Diagrams included (Mermaid/images)' },
|
|
56
|
+
{ id: 'has_examples', description: 'Examples or code samples' },
|
|
57
|
+
{ id: 'low_ambiguity', description: 'Low ambiguity (minimal should/might/could)' }
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
governance: {
|
|
61
|
+
weight: 0.15,
|
|
62
|
+
checks: [
|
|
63
|
+
{ id: 'has_approval', description: 'Phase gate approval section' },
|
|
64
|
+
{ id: 'is_approved', description: 'Actually approved (checkboxes checked)' },
|
|
65
|
+
{ id: 'has_dates', description: 'Dates and timestamps present' }
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
enterprise: {
|
|
69
|
+
weight: 0.10,
|
|
70
|
+
checks: [
|
|
71
|
+
{ id: 'has_nfrs', description: 'Non-functional requirements addressed' },
|
|
72
|
+
{ id: 'has_security', description: 'Security considerations' },
|
|
73
|
+
{ id: 'has_compliance', description: 'Compliance/regulatory mentions' }
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Run maturity checks on content.
|
|
80
|
+
* @param {string} content
|
|
81
|
+
* @returns {object}
|
|
82
|
+
*/
|
|
83
|
+
function runMaturityChecks(content) {
|
|
84
|
+
const results = {};
|
|
85
|
+
const lines = content.split('\n');
|
|
86
|
+
|
|
87
|
+
// Structure checks
|
|
88
|
+
results.has_frontmatter = content.startsWith('---');
|
|
89
|
+
results.has_toc = /table of contents|## contents|## toc/i.test(content);
|
|
90
|
+
const headings = lines.filter(l => /^#{1,4}\s+/.test(l));
|
|
91
|
+
results.has_sections = headings.length >= 3;
|
|
92
|
+
|
|
93
|
+
let emptyCount = 0;
|
|
94
|
+
let currentHeading = null;
|
|
95
|
+
let hasContentAfterHeading = false;
|
|
96
|
+
for (const line of lines) {
|
|
97
|
+
if (/^#{1,4}\s+/.test(line)) {
|
|
98
|
+
if (currentHeading && !hasContentAfterHeading) emptyCount++;
|
|
99
|
+
currentHeading = line;
|
|
100
|
+
hasContentAfterHeading = false;
|
|
101
|
+
} else if (line.trim().length > 0 && currentHeading) {
|
|
102
|
+
hasContentAfterHeading = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
results.no_empty_sections = emptyCount === 0;
|
|
106
|
+
|
|
107
|
+
// Completeness checks
|
|
108
|
+
results.no_placeholders = !/\[TODO\]|\[TBD\]|\[PLACEHOLDER\]/i.test(content);
|
|
109
|
+
results.no_clarifications = !/\[NEEDS CLARIFICATION/i.test(content);
|
|
110
|
+
results.sufficient_length = content.length > 1000;
|
|
111
|
+
|
|
112
|
+
const paragraphs = content.split(/\n\n+/).filter(p => p.trim().length > 0 && !/^#/.test(p.trim()));
|
|
113
|
+
const avgWords = paragraphs.length > 0
|
|
114
|
+
? paragraphs.reduce((sum, p) => sum + p.split(/\s+/).length, 0) / paragraphs.length
|
|
115
|
+
: 0;
|
|
116
|
+
results.has_detail = avgWords > 20;
|
|
117
|
+
|
|
118
|
+
// Traceability checks
|
|
119
|
+
results.has_requirement_ids = /\b(REQ-\d+|E\d+-S\d+|NFR-\d+|UC-\d+|FR-\d+|AC-\d+|M\d+-T\d+)\b/.test(content);
|
|
120
|
+
results.has_cross_refs = /\[.*\]\(.*\.md\)/.test(content);
|
|
121
|
+
results.has_version = /version[:\s]+\d+/i.test(content) || /^version:/m.test(content);
|
|
122
|
+
|
|
123
|
+
// Quality checks
|
|
124
|
+
results.has_acceptance_criteria = /acceptance\s+criteria/i.test(content);
|
|
125
|
+
results.has_diagrams = /```mermaid|flowchart|sequenceDiagram|classDiagram|\!\[.*\]\(.*\)/i.test(content);
|
|
126
|
+
results.has_examples = /```\w+/.test(content) || /example[:\s]/i.test(content);
|
|
127
|
+
|
|
128
|
+
const ambiguousTerms = ['should', 'might', 'could', 'may', 'possibly', 'potentially', 'TBD', 'TBA'];
|
|
129
|
+
let ambiguousCount = 0;
|
|
130
|
+
for (const term of ambiguousTerms) {
|
|
131
|
+
const pattern = new RegExp(`\\b${term}\\b`, 'gi');
|
|
132
|
+
ambiguousCount += (content.match(pattern) || []).length;
|
|
133
|
+
}
|
|
134
|
+
const wordCount = content.split(/\s+/).length;
|
|
135
|
+
results.low_ambiguity = wordCount > 0 ? (ambiguousCount / wordCount) < 0.02 : true;
|
|
136
|
+
|
|
137
|
+
// Governance checks
|
|
138
|
+
results.has_approval = /Phase Gate Approval/i.test(content);
|
|
139
|
+
results.is_approved = /- \[x\]/i.test(content) && /Approved by[:\s]+(?!Pending)/i.test(content);
|
|
140
|
+
results.has_dates = /\d{4}-\d{2}-\d{2}/.test(content);
|
|
141
|
+
|
|
142
|
+
// Enterprise checks
|
|
143
|
+
results.has_nfrs = /non-functional|NFR|performance|scalab|availab/i.test(content);
|
|
144
|
+
results.has_security = /security|auth|encryption|OWASP/i.test(content);
|
|
145
|
+
results.has_compliance = /compliance|regulatory|GDPR|HIPAA|SOC|audit/i.test(content);
|
|
146
|
+
|
|
147
|
+
return results;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Assess maturity of an artifact.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} content - Artifact content.
|
|
154
|
+
* @param {object} [options]
|
|
155
|
+
* @returns {object}
|
|
156
|
+
*/
|
|
157
|
+
function assessMaturity(content, options = {}) {
|
|
158
|
+
const checkResults = runMaturityChecks(content);
|
|
159
|
+
const categoryScores = {};
|
|
160
|
+
let totalWeightedScore = 0;
|
|
161
|
+
const gaps = [];
|
|
162
|
+
|
|
163
|
+
for (const [category, config] of Object.entries(MATURITY_CRITERIA)) {
|
|
164
|
+
const passed = config.checks.filter(c => checkResults[c.id]);
|
|
165
|
+
const score = config.checks.length > 0
|
|
166
|
+
? Math.round((passed.length / config.checks.length) * 100)
|
|
167
|
+
: 0;
|
|
168
|
+
|
|
169
|
+
categoryScores[category] = {
|
|
170
|
+
score,
|
|
171
|
+
passed: passed.length,
|
|
172
|
+
total: config.checks.length,
|
|
173
|
+
weight: config.weight
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
totalWeightedScore += score * config.weight;
|
|
177
|
+
|
|
178
|
+
// Record gaps
|
|
179
|
+
for (const check of config.checks) {
|
|
180
|
+
if (!checkResults[check.id]) {
|
|
181
|
+
gaps.push({
|
|
182
|
+
category,
|
|
183
|
+
check: check.id,
|
|
184
|
+
description: check.description
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const overallScore = Math.round(totalWeightedScore);
|
|
191
|
+
const maturityLevel = [...MATURITY_LEVELS].reverse().find(l => overallScore >= l.min_score) || MATURITY_LEVELS[0];
|
|
192
|
+
|
|
193
|
+
// Compute next level and what's needed
|
|
194
|
+
const nextLevel = MATURITY_LEVELS.find(l => l.min_score > overallScore);
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
success: true,
|
|
198
|
+
overall_score: overallScore,
|
|
199
|
+
maturity_level: maturityLevel.level,
|
|
200
|
+
maturity_name: maturityLevel.name,
|
|
201
|
+
maturity_description: maturityLevel.description,
|
|
202
|
+
category_scores: categoryScores,
|
|
203
|
+
gaps,
|
|
204
|
+
next_level: nextLevel ? {
|
|
205
|
+
level: nextLevel.level,
|
|
206
|
+
name: nextLevel.name,
|
|
207
|
+
points_needed: nextLevel.min_score - overallScore,
|
|
208
|
+
description: nextLevel.description
|
|
209
|
+
} : null,
|
|
210
|
+
total_checks: Object.values(MATURITY_CRITERIA).reduce((sum, c) => sum + c.checks.length, 0),
|
|
211
|
+
checks_passed: Object.values(MATURITY_CRITERIA).reduce((sum, c) =>
|
|
212
|
+
sum + c.checks.filter(ck => checkResults[ck.id]).length, 0
|
|
213
|
+
)
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Assess maturity of a file.
|
|
219
|
+
*
|
|
220
|
+
* @param {string} filePath
|
|
221
|
+
* @param {object} [options]
|
|
222
|
+
* @returns {object}
|
|
223
|
+
*/
|
|
224
|
+
function assessFile(filePath, options = {}) {
|
|
225
|
+
if (!fs.existsSync(filePath)) {
|
|
226
|
+
return { success: false, error: `File not found: ${filePath}` };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
230
|
+
const result = assessMaturity(content, options);
|
|
231
|
+
result.file = filePath;
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Assess maturity across all spec artifacts.
|
|
237
|
+
*
|
|
238
|
+
* @param {string} root - Project root.
|
|
239
|
+
* @param {object} [options]
|
|
240
|
+
* @returns {object}
|
|
241
|
+
*/
|
|
242
|
+
function assessProject(root, options = {}) {
|
|
243
|
+
const specsDir = path.join(root, 'specs');
|
|
244
|
+
if (!fs.existsSync(specsDir)) {
|
|
245
|
+
return { success: false, error: 'specs/ directory not found' };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const artifacts = ['challenger-brief.md', 'product-brief.md', 'prd.md', 'architecture.md', 'implementation-plan.md'];
|
|
249
|
+
const results = [];
|
|
250
|
+
|
|
251
|
+
for (const artifact of artifacts) {
|
|
252
|
+
const fullPath = path.join(specsDir, artifact);
|
|
253
|
+
if (fs.existsSync(fullPath)) {
|
|
254
|
+
const result = assessFile(fullPath, options);
|
|
255
|
+
results.push({ artifact, ...result });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const avgScore = results.length > 0
|
|
260
|
+
? Math.round(results.reduce((sum, r) => sum + (r.overall_score || 0), 0) / results.length)
|
|
261
|
+
: 0;
|
|
262
|
+
|
|
263
|
+
const avgLevel = [...MATURITY_LEVELS].reverse().find(l => avgScore >= l.min_score) || MATURITY_LEVELS[0];
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
success: true,
|
|
267
|
+
project_score: avgScore,
|
|
268
|
+
project_maturity: avgLevel.name,
|
|
269
|
+
project_level: avgLevel.level,
|
|
270
|
+
artifacts: results,
|
|
271
|
+
summary: {
|
|
272
|
+
artifacts_assessed: results.length,
|
|
273
|
+
average_score: avgScore,
|
|
274
|
+
production_ready: results.filter(r => (r.maturity_level || 0) >= 5).length,
|
|
275
|
+
draft: results.filter(r => (r.maturity_level || 0) <= 1).length
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
module.exports = {
|
|
281
|
+
runMaturityChecks,
|
|
282
|
+
assessMaturity,
|
|
283
|
+
assessFile,
|
|
284
|
+
assessProject,
|
|
285
|
+
MATURITY_LEVELS,
|
|
286
|
+
MATURITY_CRITERIA
|
|
287
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sre-integration.js — SRE Integration (Item 94)
|
|
3
|
+
*
|
|
4
|
+
* Generate monitors, alerts, runbooks, dashboards,
|
|
5
|
+
* and error-budget alignment.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node bin/lib/sre-integration.js generate|configure|report [options]
|
|
9
|
+
*
|
|
10
|
+
* State file: .jumpstart/state/sre-integration.json
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
const DEFAULT_STATE_FILE = path.join('.jumpstart', 'state', 'sre-integration.json');
|
|
19
|
+
|
|
20
|
+
const MONITOR_TYPES = ['uptime', 'latency', 'error-rate', 'saturation', 'custom'];
|
|
21
|
+
const ALERT_SEVERITIES = ['critical', 'warning', 'info'];
|
|
22
|
+
|
|
23
|
+
function defaultState() {
|
|
24
|
+
return { version: '1.0.0', monitors: [], alerts: [], runbooks: [], error_budgets: [], last_updated: null };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function loadState(stateFile) {
|
|
28
|
+
const fp = stateFile || DEFAULT_STATE_FILE;
|
|
29
|
+
if (!fs.existsSync(fp)) return defaultState();
|
|
30
|
+
try { return JSON.parse(fs.readFileSync(fp, 'utf8')); }
|
|
31
|
+
catch { return defaultState(); }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function saveState(state, stateFile) {
|
|
35
|
+
const fp = stateFile || DEFAULT_STATE_FILE;
|
|
36
|
+
const dir = path.dirname(fp);
|
|
37
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
38
|
+
state.last_updated = new Date().toISOString();
|
|
39
|
+
fs.writeFileSync(fp, JSON.stringify(state, null, 2) + '\n', 'utf8');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function generateMonitor(name, type, options = {}) {
|
|
43
|
+
if (!name || !type) return { success: false, error: 'name and type are required' };
|
|
44
|
+
if (!MONITOR_TYPES.includes(type)) {
|
|
45
|
+
return { success: false, error: `Unknown type: ${type}. Valid: ${MONITOR_TYPES.join(', ')}` };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
49
|
+
const state = loadState(stateFile);
|
|
50
|
+
|
|
51
|
+
const monitor = {
|
|
52
|
+
id: `MON-${Date.now()}`,
|
|
53
|
+
name,
|
|
54
|
+
type,
|
|
55
|
+
threshold: options.threshold || null,
|
|
56
|
+
interval: options.interval || '60s',
|
|
57
|
+
service: options.service || null,
|
|
58
|
+
created_at: new Date().toISOString()
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
state.monitors.push(monitor);
|
|
62
|
+
saveState(state, stateFile);
|
|
63
|
+
|
|
64
|
+
return { success: true, monitor };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function generateAlert(name, severity, options = {}) {
|
|
68
|
+
if (!name || !severity) return { success: false, error: 'name and severity are required' };
|
|
69
|
+
if (!ALERT_SEVERITIES.includes(severity)) {
|
|
70
|
+
return { success: false, error: `Unknown severity: ${severity}. Valid: ${ALERT_SEVERITIES.join(', ')}` };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
74
|
+
const state = loadState(stateFile);
|
|
75
|
+
|
|
76
|
+
const alert = {
|
|
77
|
+
id: `ALERT-${Date.now()}`,
|
|
78
|
+
name,
|
|
79
|
+
severity,
|
|
80
|
+
condition: options.condition || null,
|
|
81
|
+
notification_channels: options.channels || [],
|
|
82
|
+
runbook_id: options.runbook_id || null,
|
|
83
|
+
created_at: new Date().toISOString()
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
state.alerts.push(alert);
|
|
87
|
+
saveState(state, stateFile);
|
|
88
|
+
|
|
89
|
+
return { success: true, alert };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function generateRunbook(name, steps, options = {}) {
|
|
93
|
+
if (!name || !steps) return { success: false, error: 'name and steps are required' };
|
|
94
|
+
|
|
95
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
96
|
+
const state = loadState(stateFile);
|
|
97
|
+
|
|
98
|
+
const runbook = {
|
|
99
|
+
id: `RB-${Date.now()}`,
|
|
100
|
+
name,
|
|
101
|
+
steps: Array.isArray(steps) ? steps.map((s, i) => ({ order: i + 1, action: s })) : [],
|
|
102
|
+
service: options.service || null,
|
|
103
|
+
created_at: new Date().toISOString()
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
state.runbooks.push(runbook);
|
|
107
|
+
saveState(state, stateFile);
|
|
108
|
+
|
|
109
|
+
return { success: true, runbook };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function configureErrorBudget(service, slo, options = {}) {
|
|
113
|
+
if (!service || !slo) return { success: false, error: 'service and slo are required' };
|
|
114
|
+
|
|
115
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
116
|
+
const state = loadState(stateFile);
|
|
117
|
+
|
|
118
|
+
const budget = {
|
|
119
|
+
id: `EB-${Date.now()}`,
|
|
120
|
+
service,
|
|
121
|
+
slo_target: slo,
|
|
122
|
+
budget_remaining: options.remaining || 100,
|
|
123
|
+
window: options.window || '30d',
|
|
124
|
+
created_at: new Date().toISOString()
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
state.error_budgets.push(budget);
|
|
128
|
+
saveState(state, stateFile);
|
|
129
|
+
|
|
130
|
+
return { success: true, error_budget: budget };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function generateReport(options = {}) {
|
|
134
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
135
|
+
const state = loadState(stateFile);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
success: true,
|
|
139
|
+
total_monitors: state.monitors.length,
|
|
140
|
+
total_alerts: state.alerts.length,
|
|
141
|
+
total_runbooks: state.runbooks.length,
|
|
142
|
+
total_error_budgets: state.error_budgets.length,
|
|
143
|
+
monitors: state.monitors,
|
|
144
|
+
alerts: state.alerts,
|
|
145
|
+
runbooks: state.runbooks,
|
|
146
|
+
error_budgets: state.error_budgets
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = {
|
|
151
|
+
generateMonitor, generateAlert, generateRunbook, configureErrorBudget, generateReport,
|
|
152
|
+
loadState, saveState, defaultState,
|
|
153
|
+
MONITOR_TYPES, ALERT_SEVERITIES
|
|
154
|
+
};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* structured-elicitation.js — Facilitated Q&A with Structured Elicitation (Item 66)
|
|
3
|
+
*
|
|
4
|
+
* Adaptive questioning based on domain, compliance, and delivery model.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node bin/lib/structured-elicitation.js start|answer|status|report [options]
|
|
8
|
+
*
|
|
9
|
+
* State file: .jumpstart/state/elicitation.json
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const DEFAULT_STATE_FILE = path.join('.jumpstart', 'state', 'elicitation.json');
|
|
18
|
+
|
|
19
|
+
const DOMAINS = ['healthcare', 'fintech', 'retail', 'manufacturing', 'public-sector', 'general'];
|
|
20
|
+
|
|
21
|
+
const QUESTION_BANKS = {
|
|
22
|
+
general: [
|
|
23
|
+
{ id: 'G1', text: 'What problem are you solving?', category: 'problem' },
|
|
24
|
+
{ id: 'G2', text: 'Who are the primary users?', category: 'users' },
|
|
25
|
+
{ id: 'G3', text: 'What are the success criteria?', category: 'success' },
|
|
26
|
+
{ id: 'G4', text: 'What are the key constraints?', category: 'constraints' },
|
|
27
|
+
{ id: 'G5', text: 'What is the timeline?', category: 'timeline' }
|
|
28
|
+
],
|
|
29
|
+
healthcare: [
|
|
30
|
+
{ id: 'H1', text: 'Is PHI (Protected Health Information) involved?', category: 'compliance' },
|
|
31
|
+
{ id: 'H2', text: 'What HIPAA controls are required?', category: 'compliance' },
|
|
32
|
+
{ id: 'H3', text: 'Are there FDA regulatory requirements?', category: 'regulatory' }
|
|
33
|
+
],
|
|
34
|
+
fintech: [
|
|
35
|
+
{ id: 'F1', text: 'What financial regulations apply (PCI-DSS, SOX)?', category: 'compliance' },
|
|
36
|
+
{ id: 'F2', text: 'Are there data residency requirements?', category: 'compliance' },
|
|
37
|
+
{ id: 'F3', text: 'What audit trail requirements exist?', category: 'audit' }
|
|
38
|
+
]
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function defaultState() {
|
|
42
|
+
return { version: '1.0.0', sessions: [], last_updated: null };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function loadState(stateFile) {
|
|
46
|
+
const fp = stateFile || DEFAULT_STATE_FILE;
|
|
47
|
+
if (!fs.existsSync(fp)) return defaultState();
|
|
48
|
+
try { return JSON.parse(fs.readFileSync(fp, 'utf8')); }
|
|
49
|
+
catch { return defaultState(); }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function saveState(state, stateFile) {
|
|
53
|
+
const fp = stateFile || DEFAULT_STATE_FILE;
|
|
54
|
+
const dir = path.dirname(fp);
|
|
55
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
56
|
+
state.last_updated = new Date().toISOString();
|
|
57
|
+
fs.writeFileSync(fp, JSON.stringify(state, null, 2) + '\n', 'utf8');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Start a structured elicitation session.
|
|
62
|
+
*/
|
|
63
|
+
function startElicitation(domain, options = {}) {
|
|
64
|
+
if (!domain) domain = 'general';
|
|
65
|
+
if (!DOMAINS.includes(domain)) {
|
|
66
|
+
return { success: false, error: `Unknown domain: ${domain}. Valid: ${DOMAINS.join(', ')}` };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
70
|
+
const state = loadState(stateFile);
|
|
71
|
+
|
|
72
|
+
const questions = [
|
|
73
|
+
...(QUESTION_BANKS.general || []),
|
|
74
|
+
...(QUESTION_BANKS[domain] || [])
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
const session = {
|
|
78
|
+
id: `ELICIT-${Date.now()}`,
|
|
79
|
+
domain,
|
|
80
|
+
status: 'active',
|
|
81
|
+
questions: questions.map(q => ({ ...q, answered: false, answer: null })),
|
|
82
|
+
created_at: new Date().toISOString()
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
state.sessions.push(session);
|
|
86
|
+
saveState(state, stateFile);
|
|
87
|
+
|
|
88
|
+
return { success: true, session };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Answer a question.
|
|
93
|
+
*/
|
|
94
|
+
function answerQuestion(sessionId, questionId, answer, options = {}) {
|
|
95
|
+
if (!sessionId || !questionId || !answer) {
|
|
96
|
+
return { success: false, error: 'sessionId, questionId, and answer are required' };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
100
|
+
const state = loadState(stateFile);
|
|
101
|
+
|
|
102
|
+
const session = state.sessions.find(s => s.id === sessionId);
|
|
103
|
+
if (!session) return { success: false, error: `Session ${sessionId} not found` };
|
|
104
|
+
|
|
105
|
+
const question = session.questions.find(q => q.id === questionId);
|
|
106
|
+
if (!question) return { success: false, error: `Question ${questionId} not found` };
|
|
107
|
+
|
|
108
|
+
question.answered = true;
|
|
109
|
+
question.answer = answer;
|
|
110
|
+
question.answered_at = new Date().toISOString();
|
|
111
|
+
|
|
112
|
+
saveState(state, stateFile);
|
|
113
|
+
|
|
114
|
+
const remaining = session.questions.filter(q => !q.answered).length;
|
|
115
|
+
return { success: true, question, remaining };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get next unanswered question.
|
|
120
|
+
*/
|
|
121
|
+
function getNextQuestion(sessionId, options = {}) {
|
|
122
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
123
|
+
const state = loadState(stateFile);
|
|
124
|
+
|
|
125
|
+
const session = state.sessions.find(s => s.id === sessionId);
|
|
126
|
+
if (!session) return { success: false, error: `Session ${sessionId} not found` };
|
|
127
|
+
|
|
128
|
+
const next = session.questions.find(q => !q.answered);
|
|
129
|
+
if (!next) return { success: true, complete: true, question: null };
|
|
130
|
+
|
|
131
|
+
return { success: true, complete: false, question: next };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Generate elicitation report.
|
|
136
|
+
*/
|
|
137
|
+
function generateReport(sessionId, options = {}) {
|
|
138
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
139
|
+
const state = loadState(stateFile);
|
|
140
|
+
|
|
141
|
+
const session = state.sessions.find(s => s.id === sessionId);
|
|
142
|
+
if (!session) return { success: false, error: `Session ${sessionId} not found` };
|
|
143
|
+
|
|
144
|
+
const answered = session.questions.filter(q => q.answered);
|
|
145
|
+
const unanswered = session.questions.filter(q => !q.answered);
|
|
146
|
+
const byCategory = {};
|
|
147
|
+
for (const q of answered) {
|
|
148
|
+
if (!byCategory[q.category]) byCategory[q.category] = [];
|
|
149
|
+
byCategory[q.category].push({ question: q.text, answer: q.answer });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
success: true,
|
|
154
|
+
domain: session.domain,
|
|
155
|
+
total_questions: session.questions.length,
|
|
156
|
+
answered: answered.length,
|
|
157
|
+
unanswered: unanswered.length,
|
|
158
|
+
completion_pct: Math.round((answered.length / session.questions.length) * 100),
|
|
159
|
+
by_category: byCategory,
|
|
160
|
+
gaps: unanswered.map(q => q.text)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
module.exports = {
|
|
165
|
+
startElicitation,
|
|
166
|
+
answerQuestion,
|
|
167
|
+
getNextQuestion,
|
|
168
|
+
generateReport,
|
|
169
|
+
loadState,
|
|
170
|
+
saveState,
|
|
171
|
+
defaultState,
|
|
172
|
+
DOMAINS,
|
|
173
|
+
QUESTION_BANKS
|
|
174
|
+
};
|