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,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* guided-handoff.js — Guided Handoff Packages by Team (Item 73)
|
|
3
|
+
*
|
|
4
|
+
* Product to engineering, engineering to QA, engineering to ops,
|
|
5
|
+
* ops to support handoff package generation.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node bin/lib/guided-handoff.js generate|list|validate [options]
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const HANDOFF_TYPES = [
|
|
17
|
+
'product-to-engineering',
|
|
18
|
+
'engineering-to-qa',
|
|
19
|
+
'engineering-to-ops',
|
|
20
|
+
'ops-to-support'
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const HANDOFF_CHECKLISTS = {
|
|
24
|
+
'product-to-engineering': {
|
|
25
|
+
label: 'Product → Engineering',
|
|
26
|
+
required: ['user_stories', 'acceptance_criteria', 'wireframes', 'priorities', 'scope_boundaries'],
|
|
27
|
+
optional: ['competitive_analysis', 'analytics_requirements', 'feature_flags']
|
|
28
|
+
},
|
|
29
|
+
'engineering-to-qa': {
|
|
30
|
+
label: 'Engineering → QA',
|
|
31
|
+
required: ['test_plan', 'api_contracts', 'environment_setup', 'known_limitations', 'test_data'],
|
|
32
|
+
optional: ['performance_baselines', 'security_considerations', 'rollback_procedures']
|
|
33
|
+
},
|
|
34
|
+
'engineering-to-ops': {
|
|
35
|
+
label: 'Engineering → Ops',
|
|
36
|
+
required: ['deployment_guide', 'runbooks', 'monitoring_config', 'rollback_procedures', 'dependencies'],
|
|
37
|
+
optional: ['load_test_results', 'capacity_planning', 'dr_procedures']
|
|
38
|
+
},
|
|
39
|
+
'ops-to-support': {
|
|
40
|
+
label: 'Ops → Support',
|
|
41
|
+
required: ['known_issues', 'troubleshooting_guide', 'escalation_paths', 'sla_details', 'faq'],
|
|
42
|
+
optional: ['common_errors', 'workarounds', 'release_notes']
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generate a handoff package.
|
|
48
|
+
*/
|
|
49
|
+
function generateHandoff(type, root, options = {}) {
|
|
50
|
+
if (!HANDOFF_TYPES.includes(type)) {
|
|
51
|
+
return { success: false, error: `Unknown handoff type: ${type}. Valid: ${HANDOFF_TYPES.join(', ')}` };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const checklist = HANDOFF_CHECKLISTS[type];
|
|
55
|
+
const items = [];
|
|
56
|
+
|
|
57
|
+
for (const req of checklist.required) {
|
|
58
|
+
items.push({ name: req, required: true, status: options[req] ? 'provided' : 'missing' });
|
|
59
|
+
}
|
|
60
|
+
for (const opt of checklist.optional) {
|
|
61
|
+
items.push({ name: opt, required: false, status: options[opt] ? 'provided' : 'not_provided' });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const missing = items.filter(i => i.required && i.status === 'missing');
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
success: true,
|
|
68
|
+
type,
|
|
69
|
+
label: checklist.label,
|
|
70
|
+
items,
|
|
71
|
+
complete: missing.length === 0,
|
|
72
|
+
missing_required: missing.map(i => i.name),
|
|
73
|
+
generated_at: new Date().toISOString()
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* List available handoff types.
|
|
79
|
+
*/
|
|
80
|
+
function listHandoffTypes() {
|
|
81
|
+
return {
|
|
82
|
+
success: true,
|
|
83
|
+
types: HANDOFF_TYPES.map(t => ({
|
|
84
|
+
id: t,
|
|
85
|
+
label: HANDOFF_CHECKLISTS[t].label,
|
|
86
|
+
required_count: HANDOFF_CHECKLISTS[t].required.length,
|
|
87
|
+
optional_count: HANDOFF_CHECKLISTS[t].optional.length
|
|
88
|
+
}))
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Validate a handoff package completeness.
|
|
94
|
+
*/
|
|
95
|
+
function validateHandoff(type, provided, options = {}) {
|
|
96
|
+
if (!HANDOFF_TYPES.includes(type)) {
|
|
97
|
+
return { success: false, error: `Unknown handoff type: ${type}` };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const checklist = HANDOFF_CHECKLISTS[type];
|
|
101
|
+
const providedSet = new Set(provided || []);
|
|
102
|
+
const missing = checklist.required.filter(r => !providedSet.has(r));
|
|
103
|
+
const coverage = checklist.required.length > 0
|
|
104
|
+
? Math.round(((checklist.required.length - missing.length) / checklist.required.length) * 100)
|
|
105
|
+
: 100;
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
success: true,
|
|
109
|
+
type,
|
|
110
|
+
complete: missing.length === 0,
|
|
111
|
+
coverage_pct: coverage,
|
|
112
|
+
missing,
|
|
113
|
+
provided: [...providedSet]
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
generateHandoff, listHandoffTypes, validateHandoff,
|
|
119
|
+
HANDOFF_TYPES, HANDOFF_CHECKLISTS
|
|
120
|
+
};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* impact-analysis.js — Agentic Change Impact Analysis
|
|
3
|
+
*
|
|
4
|
+
* Before editing, maps what requirements, tests, services, APIs, and
|
|
5
|
+
* consumers will be affected by a given change.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node bin/lib/impact-analysis.js analyze|report [--file <path>] [--symbol <name>]
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Analyse the impact of changing a given file or symbol.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} root - Project root.
|
|
20
|
+
* @param {object} target - { file?, symbol?, specId? } — what is being changed.
|
|
21
|
+
* @param {object} [options]
|
|
22
|
+
* @returns {object} Impact analysis result.
|
|
23
|
+
*/
|
|
24
|
+
function analyzeImpact(root, target, options = {}) {
|
|
25
|
+
if (!target || (!target.file && !target.symbol && !target.specId)) {
|
|
26
|
+
return { success: false, error: 'target.file, target.symbol, or target.specId is required' };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const result = {
|
|
30
|
+
target,
|
|
31
|
+
affected_requirements: [],
|
|
32
|
+
affected_tests: [],
|
|
33
|
+
affected_services: [],
|
|
34
|
+
affected_apis: [],
|
|
35
|
+
affected_consumers: [],
|
|
36
|
+
risk_level: 'low',
|
|
37
|
+
summary: {}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const specsDir = path.join(root, 'specs');
|
|
41
|
+
const testsDir = path.join(root, options.testsDir || 'tests');
|
|
42
|
+
const srcDir = path.join(root, options.srcDir || 'src');
|
|
43
|
+
|
|
44
|
+
// Determine the key to search for (filename stem, symbol name, or spec ID)
|
|
45
|
+
const searchTerms = [];
|
|
46
|
+
if (target.file) {
|
|
47
|
+
searchTerms.push(path.basename(target.file, path.extname(target.file)));
|
|
48
|
+
searchTerms.push(path.basename(target.file));
|
|
49
|
+
}
|
|
50
|
+
if (target.symbol) {
|
|
51
|
+
searchTerms.push(target.symbol);
|
|
52
|
+
}
|
|
53
|
+
if (target.specId) {
|
|
54
|
+
searchTerms.push(target.specId);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function grepDir(dir, label) {
|
|
58
|
+
if (!fs.existsSync(dir)) return [];
|
|
59
|
+
const hits = [];
|
|
60
|
+
const walk = (d) => {
|
|
61
|
+
for (const entry of fs.readdirSync(d, { withFileTypes: true })) {
|
|
62
|
+
const full = path.join(d, entry.name);
|
|
63
|
+
if (entry.isDirectory()) {
|
|
64
|
+
walk(full);
|
|
65
|
+
} else if (entry.isFile()) {
|
|
66
|
+
try {
|
|
67
|
+
const content = fs.readFileSync(full, 'utf8');
|
|
68
|
+
const rel = path.relative(root, full).replace(/\\/g, '/');
|
|
69
|
+
for (const term of searchTerms) {
|
|
70
|
+
if (content.includes(term)) {
|
|
71
|
+
hits.push({ file: rel, label, matched_term: term });
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// skip unreadable files
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
walk(dir);
|
|
82
|
+
return hits;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Find affected spec requirements
|
|
86
|
+
const specHits = grepDir(specsDir, 'requirement');
|
|
87
|
+
for (const h of specHits) {
|
|
88
|
+
if (!result.affected_requirements.some(r => r.file === h.file)) {
|
|
89
|
+
result.affected_requirements.push(h);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Find affected tests
|
|
94
|
+
const testHits = grepDir(testsDir, 'test');
|
|
95
|
+
for (const h of testHits) {
|
|
96
|
+
if (!result.affected_tests.some(r => r.file === h.file)) {
|
|
97
|
+
result.affected_tests.push(h);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Find affected services and API consumers within src
|
|
102
|
+
const srcHits = grepDir(srcDir, 'source');
|
|
103
|
+
for (const h of srcHits) {
|
|
104
|
+
// Rough heuristic: files with "service", "api", "controller", "route" in name are services/APIs
|
|
105
|
+
const lower = h.file.toLowerCase();
|
|
106
|
+
if (lower.includes('service') || lower.includes('controller') || lower.includes('route')) {
|
|
107
|
+
result.affected_services.push(h);
|
|
108
|
+
} else if (lower.includes('api') || lower.includes('endpoint') || lower.includes('handler')) {
|
|
109
|
+
result.affected_apis.push(h);
|
|
110
|
+
} else {
|
|
111
|
+
result.affected_consumers.push(h);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Compute risk level
|
|
116
|
+
const totalAffected =
|
|
117
|
+
result.affected_requirements.length +
|
|
118
|
+
result.affected_tests.length +
|
|
119
|
+
result.affected_services.length +
|
|
120
|
+
result.affected_apis.length +
|
|
121
|
+
result.affected_consumers.length;
|
|
122
|
+
|
|
123
|
+
if (totalAffected > 20) {
|
|
124
|
+
result.risk_level = 'critical';
|
|
125
|
+
} else if (totalAffected > 10) {
|
|
126
|
+
result.risk_level = 'high';
|
|
127
|
+
} else if (totalAffected > 4) {
|
|
128
|
+
result.risk_level = 'medium';
|
|
129
|
+
} else {
|
|
130
|
+
result.risk_level = 'low';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
result.summary = {
|
|
134
|
+
total_affected: totalAffected,
|
|
135
|
+
requirements: result.affected_requirements.length,
|
|
136
|
+
tests: result.affected_tests.length,
|
|
137
|
+
services: result.affected_services.length,
|
|
138
|
+
apis: result.affected_apis.length,
|
|
139
|
+
consumers: result.affected_consumers.length,
|
|
140
|
+
risk_level: result.risk_level
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return { success: true, ...result };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Render a human-readable impact report.
|
|
148
|
+
*
|
|
149
|
+
* @param {object} analysis - Result from analyzeImpact().
|
|
150
|
+
* @returns {string}
|
|
151
|
+
*/
|
|
152
|
+
function renderImpactReport(analysis) {
|
|
153
|
+
if (!analysis.success) {
|
|
154
|
+
return `❌ Impact analysis failed: ${analysis.error}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const lines = [];
|
|
158
|
+
const riskEmoji = { low: '🟢', medium: '🟡', high: '🟠', critical: '🔴' };
|
|
159
|
+
const emoji = riskEmoji[analysis.risk_level] || '⚪';
|
|
160
|
+
|
|
161
|
+
lines.push(`\n${emoji} Impact Analysis — Risk: ${analysis.risk_level.toUpperCase()}`);
|
|
162
|
+
lines.push(`\nTarget: ${JSON.stringify(analysis.target)}`);
|
|
163
|
+
lines.push(`Total affected: ${analysis.summary.total_affected}`);
|
|
164
|
+
lines.push('');
|
|
165
|
+
|
|
166
|
+
const sections = [
|
|
167
|
+
['Requirements', analysis.affected_requirements],
|
|
168
|
+
['Tests', analysis.affected_tests],
|
|
169
|
+
['Services', analysis.affected_services],
|
|
170
|
+
['APIs', analysis.affected_apis],
|
|
171
|
+
['Consumers', analysis.affected_consumers]
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
for (const [label, items] of sections) {
|
|
175
|
+
if (items.length > 0) {
|
|
176
|
+
lines.push(`${label} (${items.length}):`);
|
|
177
|
+
for (const item of items) {
|
|
178
|
+
lines.push(` • ${item.file}`);
|
|
179
|
+
}
|
|
180
|
+
lines.push('');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return lines.join('\n');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
module.exports = {
|
|
188
|
+
analyzeImpact,
|
|
189
|
+
renderImpactReport
|
|
190
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* incident-feedback.js — Incident-to-Spec Feedback Loop (Item 52)
|
|
3
|
+
*
|
|
4
|
+
* Convert production incidents into requirements, NFR updates,
|
|
5
|
+
* and architecture improvements.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node bin/lib/incident-feedback.js log|analyze|report [options]
|
|
9
|
+
*
|
|
10
|
+
* State file: .jumpstart/state/incidents.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', 'incidents.json');
|
|
19
|
+
|
|
20
|
+
const INCIDENT_SEVERITIES = ['sev1', 'sev2', 'sev3', 'sev4'];
|
|
21
|
+
const INCIDENT_CATEGORIES = ['availability', 'performance', 'security', 'data-loss', 'functionality', 'ux'];
|
|
22
|
+
|
|
23
|
+
function defaultState() {
|
|
24
|
+
return {
|
|
25
|
+
version: '1.0.0',
|
|
26
|
+
created_at: new Date().toISOString(),
|
|
27
|
+
last_updated: null,
|
|
28
|
+
incidents: [],
|
|
29
|
+
spec_updates: []
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function loadState(stateFile) {
|
|
34
|
+
const filePath = stateFile || DEFAULT_STATE_FILE;
|
|
35
|
+
if (!fs.existsSync(filePath)) return defaultState();
|
|
36
|
+
try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
|
|
37
|
+
catch { return defaultState(); }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function saveState(state, stateFile) {
|
|
41
|
+
const filePath = stateFile || DEFAULT_STATE_FILE;
|
|
42
|
+
const dir = path.dirname(filePath);
|
|
43
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
44
|
+
state.last_updated = new Date().toISOString();
|
|
45
|
+
fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Log a production incident.
|
|
50
|
+
*
|
|
51
|
+
* @param {object} incident - { title, severity, category, description, root_cause?, impact? }
|
|
52
|
+
* @param {object} [options]
|
|
53
|
+
* @returns {object}
|
|
54
|
+
*/
|
|
55
|
+
function logIncident(incident, options = {}) {
|
|
56
|
+
if (!incident || !incident.title || !incident.severity) {
|
|
57
|
+
return { success: false, error: 'title and severity are required' };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!INCIDENT_SEVERITIES.includes(incident.severity)) {
|
|
61
|
+
return { success: false, error: `Invalid severity. Must be one of: ${INCIDENT_SEVERITIES.join(', ')}` };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
65
|
+
const state = loadState(stateFile);
|
|
66
|
+
|
|
67
|
+
const inc = {
|
|
68
|
+
id: `INC-${(state.incidents.length + 1).toString().padStart(4, '0')}`,
|
|
69
|
+
title: incident.title,
|
|
70
|
+
severity: incident.severity,
|
|
71
|
+
category: incident.category || 'functionality',
|
|
72
|
+
description: incident.description || '',
|
|
73
|
+
root_cause: incident.root_cause || null,
|
|
74
|
+
impact: incident.impact || null,
|
|
75
|
+
spec_updates_generated: false,
|
|
76
|
+
logged_at: new Date().toISOString()
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
state.incidents.push(inc);
|
|
80
|
+
saveState(state, stateFile);
|
|
81
|
+
|
|
82
|
+
return { success: true, incident: inc };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Analyze incident and generate spec update recommendations.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} incidentId
|
|
89
|
+
* @param {object} [options]
|
|
90
|
+
* @returns {object}
|
|
91
|
+
*/
|
|
92
|
+
function analyzeIncident(incidentId, options = {}) {
|
|
93
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
94
|
+
const state = loadState(stateFile);
|
|
95
|
+
|
|
96
|
+
const incident = state.incidents.find(i => i.id === incidentId);
|
|
97
|
+
if (!incident) return { success: false, error: `Incident not found: ${incidentId}` };
|
|
98
|
+
|
|
99
|
+
const recommendations = [];
|
|
100
|
+
|
|
101
|
+
if (incident.category === 'availability') {
|
|
102
|
+
recommendations.push({ type: 'nfr', spec: 'architecture.md', update: 'Add/update availability SLO', priority: 'high' });
|
|
103
|
+
recommendations.push({ type: 'requirement', spec: 'prd.md', update: 'Add monitoring requirement', priority: 'medium' });
|
|
104
|
+
}
|
|
105
|
+
if (incident.category === 'performance') {
|
|
106
|
+
recommendations.push({ type: 'nfr', spec: 'architecture.md', update: 'Add/update latency SLO', priority: 'high' });
|
|
107
|
+
}
|
|
108
|
+
if (incident.category === 'security') {
|
|
109
|
+
recommendations.push({ type: 'requirement', spec: 'prd.md', update: 'Add security hardening requirement', priority: 'critical' });
|
|
110
|
+
recommendations.push({ type: 'architecture', spec: 'architecture.md', update: 'Review security architecture', priority: 'high' });
|
|
111
|
+
}
|
|
112
|
+
if (incident.severity === 'sev1') {
|
|
113
|
+
recommendations.push({ type: 'architecture', spec: 'architecture.md', update: 'Add circuit breaker or failover', priority: 'critical' });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
incident.spec_updates_generated = true;
|
|
117
|
+
state.spec_updates.push({
|
|
118
|
+
incident_id: incidentId,
|
|
119
|
+
recommendations,
|
|
120
|
+
generated_at: new Date().toISOString()
|
|
121
|
+
});
|
|
122
|
+
saveState(state, stateFile);
|
|
123
|
+
|
|
124
|
+
return { success: true, incident_id: incidentId, recommendations, total: recommendations.length };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Generate incident feedback report.
|
|
129
|
+
*
|
|
130
|
+
* @param {object} [options]
|
|
131
|
+
* @returns {object}
|
|
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_incidents: state.incidents.length,
|
|
140
|
+
by_severity: state.incidents.reduce((acc, i) => { acc[i.severity] = (acc[i.severity] || 0) + 1; return acc; }, {}),
|
|
141
|
+
by_category: state.incidents.reduce((acc, i) => { acc[i.category] = (acc[i.category] || 0) + 1; return acc; }, {}),
|
|
142
|
+
spec_updates_pending: state.incidents.filter(i => !i.spec_updates_generated).length,
|
|
143
|
+
total_spec_updates: state.spec_updates.length,
|
|
144
|
+
incidents: state.incidents
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
defaultState,
|
|
150
|
+
loadState,
|
|
151
|
+
saveState,
|
|
152
|
+
logIncident,
|
|
153
|
+
analyzeIncident,
|
|
154
|
+
generateReport,
|
|
155
|
+
INCIDENT_SEVERITIES,
|
|
156
|
+
INCIDENT_CATEGORIES
|
|
157
|
+
};
|
package/bin/lib/integrate.js
CHANGED
|
@@ -88,7 +88,7 @@ function parseSkillFrontmatter(content) {
|
|
|
88
88
|
result.triggers = triggerMatch[1]
|
|
89
89
|
.split('\n')
|
|
90
90
|
.map((l) => l.replace(/^[-*]\s*/, '').trim())
|
|
91
|
-
.filter((l) => l.length > 0
|
|
91
|
+
.filter((l) => l.length > 0);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
return result;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* knowledge-graph.js — Knowledge Graph Across Initiatives (Item 81)
|
|
3
|
+
*
|
|
4
|
+
* Reuse patterns, controls, decisions, skills, and modules
|
|
5
|
+
* across the enterprise.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node bin/lib/knowledge-graph.js add|query|report [options]
|
|
9
|
+
*
|
|
10
|
+
* State file: .jumpstart/state/knowledge-graph.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', 'knowledge-graph.json');
|
|
19
|
+
|
|
20
|
+
const NODE_TYPES = ['pattern', 'decision', 'control', 'component', 'skill', 'module'];
|
|
21
|
+
const EDGE_TYPES = ['uses', 'implements', 'depends-on', 'related-to', 'supersedes'];
|
|
22
|
+
|
|
23
|
+
function defaultState() {
|
|
24
|
+
return { version: '1.0.0', nodes: [], edges: [], 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 addNode(name, type, options = {}) {
|
|
43
|
+
if (!name || !type) return { success: false, error: 'name and type are required' };
|
|
44
|
+
if (!NODE_TYPES.includes(type)) {
|
|
45
|
+
return { success: false, error: `Unknown type: ${type}. Valid: ${NODE_TYPES.join(', ')}` };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
49
|
+
const state = loadState(stateFile);
|
|
50
|
+
|
|
51
|
+
const node = {
|
|
52
|
+
id: `KG-${Date.now()}`,
|
|
53
|
+
name,
|
|
54
|
+
type,
|
|
55
|
+
tags: options.tags || [],
|
|
56
|
+
metadata: options.metadata || {},
|
|
57
|
+
created_at: new Date().toISOString()
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
state.nodes.push(node);
|
|
61
|
+
saveState(state, stateFile);
|
|
62
|
+
|
|
63
|
+
return { success: true, node };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function addEdge(fromId, toId, edgeType, options = {}) {
|
|
67
|
+
if (!fromId || !toId || !edgeType) return { success: false, error: 'fromId, toId, and edgeType are required' };
|
|
68
|
+
if (!EDGE_TYPES.includes(edgeType)) {
|
|
69
|
+
return { success: false, error: `Unknown edge type: ${edgeType}. Valid: ${EDGE_TYPES.join(', ')}` };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
73
|
+
const state = loadState(stateFile);
|
|
74
|
+
|
|
75
|
+
const edge = { from: fromId, to: toId, type: edgeType, created_at: new Date().toISOString() };
|
|
76
|
+
state.edges.push(edge);
|
|
77
|
+
saveState(state, stateFile);
|
|
78
|
+
|
|
79
|
+
return { success: true, edge };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function queryGraph(options = {}) {
|
|
83
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
84
|
+
const state = loadState(stateFile);
|
|
85
|
+
|
|
86
|
+
let nodes = state.nodes;
|
|
87
|
+
if (options.type) nodes = nodes.filter(n => n.type === options.type);
|
|
88
|
+
if (options.tag) nodes = nodes.filter(n => n.tags.includes(options.tag));
|
|
89
|
+
if (options.search) {
|
|
90
|
+
const q = options.search.toLowerCase();
|
|
91
|
+
nodes = nodes.filter(n => n.name.toLowerCase().includes(q));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
success: true,
|
|
96
|
+
nodes: nodes.length,
|
|
97
|
+
edges: state.edges.length,
|
|
98
|
+
results: nodes,
|
|
99
|
+
related_edges: state.edges.filter(e => nodes.some(n => n.id === e.from || n.id === e.to))
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function generateReport(options = {}) {
|
|
104
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
105
|
+
const state = loadState(stateFile);
|
|
106
|
+
|
|
107
|
+
const byType = {};
|
|
108
|
+
for (const n of state.nodes) { byType[n.type] = (byType[n.type] || 0) + 1; }
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
success: true,
|
|
112
|
+
total_nodes: state.nodes.length,
|
|
113
|
+
total_edges: state.edges.length,
|
|
114
|
+
by_type: byType
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = {
|
|
119
|
+
addNode, addEdge, queryGraph, generateReport,
|
|
120
|
+
loadState, saveState, defaultState,
|
|
121
|
+
NODE_TYPES, EDGE_TYPES
|
|
122
|
+
};
|