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,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* multi-repo.js — Multi-Repo Program Orchestration
|
|
3
|
+
*
|
|
4
|
+
* Lets one initiative span frontend, backend, infra, data, and docs
|
|
5
|
+
* repos with shared specs, dependencies, and release plans.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node bin/lib/multi-repo.js init|status|link|plan [options]
|
|
9
|
+
*
|
|
10
|
+
* State file: .jumpstart/state/multi-repo.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', 'multi-repo.json');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Load the multi-repo state from disk.
|
|
22
|
+
* @param {string} [stateFile]
|
|
23
|
+
* @returns {object}
|
|
24
|
+
*/
|
|
25
|
+
function loadMultiRepoState(stateFile) {
|
|
26
|
+
const filePath = stateFile || DEFAULT_STATE_FILE;
|
|
27
|
+
if (!fs.existsSync(filePath)) {
|
|
28
|
+
return defaultMultiRepoState();
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
32
|
+
} catch {
|
|
33
|
+
return defaultMultiRepoState();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Default multi-repo state structure.
|
|
39
|
+
* @returns {object}
|
|
40
|
+
*/
|
|
41
|
+
function defaultMultiRepoState() {
|
|
42
|
+
return {
|
|
43
|
+
version: '1.0.0',
|
|
44
|
+
program_name: null,
|
|
45
|
+
created_at: new Date().toISOString(),
|
|
46
|
+
last_updated: null,
|
|
47
|
+
repos: [],
|
|
48
|
+
shared_specs: [],
|
|
49
|
+
dependencies: [],
|
|
50
|
+
release_plan: {
|
|
51
|
+
milestones: [],
|
|
52
|
+
current_milestone: null
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Save the multi-repo state to disk.
|
|
59
|
+
* @param {object} state
|
|
60
|
+
* @param {string} [stateFile]
|
|
61
|
+
*/
|
|
62
|
+
function saveMultiRepoState(state, stateFile) {
|
|
63
|
+
const filePath = stateFile || DEFAULT_STATE_FILE;
|
|
64
|
+
const dir = path.dirname(filePath);
|
|
65
|
+
if (!fs.existsSync(dir)) {
|
|
66
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
state.last_updated = new Date().toISOString();
|
|
69
|
+
fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Initialize a multi-repo program.
|
|
74
|
+
* @param {string} programName
|
|
75
|
+
* @param {object} [options]
|
|
76
|
+
* @returns {object}
|
|
77
|
+
*/
|
|
78
|
+
function initProgram(programName, options = {}) {
|
|
79
|
+
if (!programName || typeof programName !== 'string' || !programName.trim()) {
|
|
80
|
+
return { success: false, error: 'Program name is required' };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
84
|
+
const state = defaultMultiRepoState();
|
|
85
|
+
state.program_name = programName.trim();
|
|
86
|
+
|
|
87
|
+
saveMultiRepoState(state, stateFile);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
success: true,
|
|
91
|
+
program_name: state.program_name,
|
|
92
|
+
state_file: stateFile,
|
|
93
|
+
message: `Program "${state.program_name}" initialized`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Link a repo to the current program.
|
|
99
|
+
* @param {string} repoUrl - Git URL or local path of the repo.
|
|
100
|
+
* @param {string} role - Role of this repo: frontend|backend|infra|data|docs|other.
|
|
101
|
+
* @param {object} [options]
|
|
102
|
+
* @returns {object}
|
|
103
|
+
*/
|
|
104
|
+
function linkRepo(repoUrl, role, options = {}) {
|
|
105
|
+
if (!repoUrl) return { success: false, error: 'repoUrl is required' };
|
|
106
|
+
|
|
107
|
+
const validRoles = ['frontend', 'backend', 'infra', 'data', 'docs', 'other'];
|
|
108
|
+
const normalizedRole = (role || 'other').toLowerCase();
|
|
109
|
+
if (!validRoles.includes(normalizedRole)) {
|
|
110
|
+
return { success: false, error: `role must be one of: ${validRoles.join(', ')}` };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
114
|
+
const state = loadMultiRepoState(stateFile);
|
|
115
|
+
|
|
116
|
+
const existing = state.repos.find(r => r.url === repoUrl);
|
|
117
|
+
if (existing) {
|
|
118
|
+
return { success: false, error: `Repo already linked: ${repoUrl}` };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const entry = {
|
|
122
|
+
id: `repo-${Date.now()}`,
|
|
123
|
+
url: repoUrl,
|
|
124
|
+
role: normalizedRole,
|
|
125
|
+
linked_at: new Date().toISOString(),
|
|
126
|
+
specs: [],
|
|
127
|
+
status: 'active'
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
state.repos.push(entry);
|
|
131
|
+
saveMultiRepoState(state, stateFile);
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
success: true,
|
|
135
|
+
repo: entry,
|
|
136
|
+
total_repos: state.repos.length
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Add a shared spec reference across repos.
|
|
142
|
+
* @param {string} specPath - Relative path to shared spec file.
|
|
143
|
+
* @param {string[]} repoIds - IDs of repos this spec applies to.
|
|
144
|
+
* @param {object} [options]
|
|
145
|
+
* @returns {object}
|
|
146
|
+
*/
|
|
147
|
+
function addSharedSpec(specPath, repoIds, options = {}) {
|
|
148
|
+
if (!specPath) return { success: false, error: 'specPath is required' };
|
|
149
|
+
|
|
150
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
151
|
+
const state = loadMultiRepoState(stateFile);
|
|
152
|
+
|
|
153
|
+
const entry = {
|
|
154
|
+
id: `spec-${Date.now()}`,
|
|
155
|
+
path: specPath,
|
|
156
|
+
repos: Array.isArray(repoIds) ? repoIds : [],
|
|
157
|
+
added_at: new Date().toISOString()
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
state.shared_specs.push(entry);
|
|
161
|
+
saveMultiRepoState(state, stateFile);
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
success: true,
|
|
165
|
+
spec: entry,
|
|
166
|
+
total_shared_specs: state.shared_specs.length
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Record a cross-repo dependency.
|
|
172
|
+
* @param {string} fromRepoId - Source repo ID.
|
|
173
|
+
* @param {string} toRepoId - Target repo ID.
|
|
174
|
+
* @param {string} dependencyType - Type: api|data|event|deploy|other.
|
|
175
|
+
* @param {object} [options]
|
|
176
|
+
* @returns {object}
|
|
177
|
+
*/
|
|
178
|
+
function addDependency(fromRepoId, toRepoId, dependencyType, options = {}) {
|
|
179
|
+
if (!fromRepoId || !toRepoId) {
|
|
180
|
+
return { success: false, error: 'fromRepoId and toRepoId are required' };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
184
|
+
const state = loadMultiRepoState(stateFile);
|
|
185
|
+
|
|
186
|
+
const dep = {
|
|
187
|
+
id: `dep-${Date.now()}`,
|
|
188
|
+
from: fromRepoId,
|
|
189
|
+
to: toRepoId,
|
|
190
|
+
type: dependencyType || 'other',
|
|
191
|
+
created_at: new Date().toISOString()
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
state.dependencies.push(dep);
|
|
195
|
+
saveMultiRepoState(state, stateFile);
|
|
196
|
+
|
|
197
|
+
return { success: true, dependency: dep };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get the current status of the multi-repo program.
|
|
202
|
+
* @param {object} [options]
|
|
203
|
+
* @returns {object}
|
|
204
|
+
*/
|
|
205
|
+
function getProgramStatus(options = {}) {
|
|
206
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
207
|
+
const state = loadMultiRepoState(stateFile);
|
|
208
|
+
|
|
209
|
+
const roleBreakdown = {};
|
|
210
|
+
for (const repo of state.repos) {
|
|
211
|
+
roleBreakdown[repo.role] = (roleBreakdown[repo.role] || 0) + 1;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
program_name: state.program_name,
|
|
216
|
+
initialized: !!state.program_name,
|
|
217
|
+
repo_count: state.repos.length,
|
|
218
|
+
shared_spec_count: state.shared_specs.length,
|
|
219
|
+
dependency_count: state.dependencies.length,
|
|
220
|
+
role_breakdown: roleBreakdown,
|
|
221
|
+
repos: state.repos,
|
|
222
|
+
release_plan: state.release_plan,
|
|
223
|
+
last_updated: state.last_updated
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Generate a release plan across all linked repos.
|
|
229
|
+
* @param {object[]} milestones - Array of { name, target_date, repos[] }.
|
|
230
|
+
* @param {object} [options]
|
|
231
|
+
* @returns {object}
|
|
232
|
+
*/
|
|
233
|
+
function setReleasePlan(milestones, options = {}) {
|
|
234
|
+
if (!Array.isArray(milestones)) {
|
|
235
|
+
return { success: false, error: 'milestones must be an array' };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
239
|
+
const state = loadMultiRepoState(stateFile);
|
|
240
|
+
|
|
241
|
+
state.release_plan.milestones = milestones.map((m, i) => ({
|
|
242
|
+
id: `milestone-${i + 1}`,
|
|
243
|
+
name: m.name || `Milestone ${i + 1}`,
|
|
244
|
+
target_date: m.target_date || null,
|
|
245
|
+
repos: m.repos || [],
|
|
246
|
+
status: m.status || 'planned'
|
|
247
|
+
}));
|
|
248
|
+
|
|
249
|
+
if (state.release_plan.milestones.length > 0) {
|
|
250
|
+
state.release_plan.current_milestone = state.release_plan.milestones[0].id;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
saveMultiRepoState(state, stateFile);
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
success: true,
|
|
257
|
+
milestone_count: state.release_plan.milestones.length,
|
|
258
|
+
release_plan: state.release_plan
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
module.exports = {
|
|
263
|
+
loadMultiRepoState,
|
|
264
|
+
saveMultiRepoState,
|
|
265
|
+
defaultMultiRepoState,
|
|
266
|
+
initProgram,
|
|
267
|
+
linkRepo,
|
|
268
|
+
addSharedSpec,
|
|
269
|
+
addDependency,
|
|
270
|
+
getProgramStatus,
|
|
271
|
+
setReleasePlan
|
|
272
|
+
};
|
package/bin/lib/next-phase.js
CHANGED
|
@@ -31,6 +31,7 @@ const { join, resolve } = require('path');
|
|
|
31
31
|
import { loadState } from './state-store.js';
|
|
32
32
|
import { getHandoff, isArtifactApproved } from './handoff.js';
|
|
33
33
|
import { parseSimpleYaml } from './config-loader.js';
|
|
34
|
+
import { readFocusFromConfig, isPhaseInFocus } from './focus.js';
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* Phase-to-slash-command map.
|
|
@@ -121,6 +122,9 @@ export function determineNextAction(options = {}) {
|
|
|
121
122
|
const projectType = config?.project?.type || 'greenfield';
|
|
122
123
|
const requireApproval = config?.workflow?.require_gate_approval !== false;
|
|
123
124
|
|
|
125
|
+
// Load focus mode config (if active)
|
|
126
|
+
const focusConfig = readFocusFromConfig(configPath);
|
|
127
|
+
|
|
124
128
|
// Load current state
|
|
125
129
|
const state = loadState(statePath);
|
|
126
130
|
const currentPhase = state.current_phase;
|
|
@@ -142,7 +146,8 @@ export function determineNextAction(options = {}) {
|
|
|
142
146
|
next_agent: 'analyst',
|
|
143
147
|
command: AGENT_COMMANDS.analyst,
|
|
144
148
|
message: 'Phase 0 (Challenger) is already approved. Next: Phase 1 — ' + PHASE_DESCRIPTIONS['1'],
|
|
145
|
-
context_files: getHandoff(0).context_files || []
|
|
149
|
+
context_files: getHandoff(0).context_files || [],
|
|
150
|
+
focus: focusConfig && focusConfig.enabled ? { active: true, start_phase: focusConfig.start_phase, end_phase: focusConfig.end_phase } : undefined
|
|
146
151
|
};
|
|
147
152
|
}
|
|
148
153
|
return {
|
|
@@ -153,7 +158,26 @@ export function determineNextAction(options = {}) {
|
|
|
153
158
|
command: '/jumpstart.review',
|
|
154
159
|
artifact: 'specs/challenger-brief.md',
|
|
155
160
|
message: 'Phase 0 (Challenger) artifact exists but is not yet approved. Review and approve it to proceed.',
|
|
156
|
-
context_files: []
|
|
161
|
+
context_files: [],
|
|
162
|
+
focus: focusConfig && focusConfig.enabled ? { active: true, start_phase: focusConfig.start_phase, end_phase: focusConfig.end_phase } : undefined
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// If focus mode is active, skip to the focus start phase
|
|
167
|
+
if (focusConfig && focusConfig.enabled) {
|
|
168
|
+
const focusStart = focusConfig.start_phase;
|
|
169
|
+
const agentNames = { '-1': 'scout', '0': 'challenger', '1': 'analyst', '2': 'pm', '3': 'architect', '4': 'developer' };
|
|
170
|
+
const agent = agentNames[String(focusStart)];
|
|
171
|
+
const command = AGENT_COMMANDS[agent];
|
|
172
|
+
return {
|
|
173
|
+
action: 'start',
|
|
174
|
+
current_phase: null,
|
|
175
|
+
next_phase: focusStart,
|
|
176
|
+
next_agent: agent,
|
|
177
|
+
command: command,
|
|
178
|
+
message: `Focus mode active (${focusConfig.preset || 'custom'}). Start with Phase ${focusStart} — ${PHASE_DESCRIPTIONS[String(focusStart)]}`,
|
|
179
|
+
context_files: [],
|
|
180
|
+
focus: { active: true, start_phase: focusConfig.start_phase, end_phase: focusConfig.end_phase }
|
|
157
181
|
};
|
|
158
182
|
}
|
|
159
183
|
|
|
@@ -206,17 +230,23 @@ export function determineNextAction(options = {}) {
|
|
|
206
230
|
};
|
|
207
231
|
}
|
|
208
232
|
|
|
209
|
-
// ─── Case 2: Final phase
|
|
210
|
-
if (currentPhase === 4) {
|
|
233
|
+
// ─── Case 2: Final phase or focus end reached — check completion ─────────
|
|
234
|
+
if (currentPhase === 4 || (focusConfig && focusConfig.enabled && currentPhase > focusConfig.end_phase)) {
|
|
235
|
+
const focusNote = focusConfig && focusConfig.enabled
|
|
236
|
+
? ` Focus mode (${focusConfig.preset || 'custom'}) workflow complete.`
|
|
237
|
+
: '';
|
|
211
238
|
return {
|
|
212
239
|
action: 'complete',
|
|
213
|
-
current_phase:
|
|
240
|
+
current_phase: currentPhase,
|
|
214
241
|
next_phase: null,
|
|
215
242
|
next_agent: null,
|
|
216
243
|
command: '/jumpstart.status',
|
|
217
|
-
message:
|
|
244
|
+
message: currentPhase === 4
|
|
245
|
+
? 'Phase 4 (Developer) is the final phase. All specification phases are complete. Run `/jumpstart.status` for a full project overview, or `/jumpstart.deploy` for deployment planning.'
|
|
246
|
+
: `Phase ${currentPhase} (${PHASE_NAMES[String(currentPhase)]}) reached the end of focus range.${focusNote} Run \`/jumpstart.status\` for a project overview.`,
|
|
218
247
|
suggestions: ['/jumpstart.status', '/jumpstart.deploy', '/jumpstart.resume'],
|
|
219
|
-
context_files: []
|
|
248
|
+
context_files: [],
|
|
249
|
+
focus: focusConfig && focusConfig.enabled ? { active: true, start_phase: focusConfig.start_phase, end_phase: focusConfig.end_phase } : undefined
|
|
220
250
|
};
|
|
221
251
|
}
|
|
222
252
|
|
|
@@ -278,6 +308,20 @@ export function determineNextAction(options = {}) {
|
|
|
278
308
|
const nextPhase = handoff.next_phase;
|
|
279
309
|
const command = AGENT_COMMANDS[nextAgent];
|
|
280
310
|
|
|
311
|
+
// Check if next phase is beyond focus range
|
|
312
|
+
if (focusConfig && focusConfig.enabled && !isPhaseInFocus(nextPhase, focusConfig)) {
|
|
313
|
+
return {
|
|
314
|
+
action: 'complete',
|
|
315
|
+
current_phase: currentPhase,
|
|
316
|
+
next_phase: null,
|
|
317
|
+
next_agent: null,
|
|
318
|
+
command: '/jumpstart.status',
|
|
319
|
+
message: `Phase ${currentPhase} (${PHASE_NAMES[String(currentPhase)]}) is approved! Focus mode (${focusConfig.preset || 'custom'}) workflow complete — Phase ${nextPhase} is outside the focus range. Run \`/jumpstart.status\` for a project overview.`,
|
|
320
|
+
context_files: [],
|
|
321
|
+
focus: { active: true, start_phase: focusConfig.start_phase, end_phase: focusConfig.end_phase }
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
281
325
|
return {
|
|
282
326
|
action: 'proceed',
|
|
283
327
|
current_phase: currentPhase,
|
|
@@ -285,7 +329,8 @@ export function determineNextAction(options = {}) {
|
|
|
285
329
|
next_agent: nextAgent,
|
|
286
330
|
command: command,
|
|
287
331
|
message: `Phase ${currentPhase} (${PHASE_NAMES[String(currentPhase)]}) is approved! Next: Phase ${nextPhase} — ${PHASE_DESCRIPTIONS[String(nextPhase)]}`,
|
|
288
|
-
context_files: handoff.context_files || []
|
|
332
|
+
context_files: handoff.context_files || [],
|
|
333
|
+
focus: focusConfig && focusConfig.enabled ? { active: true, start_phase: focusConfig.start_phase, end_phase: focusConfig.end_phase } : undefined
|
|
289
334
|
};
|
|
290
335
|
}
|
|
291
336
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ops-ownership.js — Operational Ownership Modeling (Item 39)
|
|
3
|
+
*
|
|
4
|
+
* Require clear service owner, escalation path, on-call model,
|
|
5
|
+
* and support plan.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node bin/lib/ops-ownership.js define|check|report [options]
|
|
9
|
+
*
|
|
10
|
+
* State file: .jumpstart/state/ops-ownership.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', 'ops-ownership.json');
|
|
19
|
+
|
|
20
|
+
const OWNERSHIP_FIELDS = ['service_owner', 'team', 'escalation_path', 'oncall_model', 'support_hours', 'runbook_url', 'sla_tier'];
|
|
21
|
+
|
|
22
|
+
function defaultState() {
|
|
23
|
+
return {
|
|
24
|
+
version: '1.0.0',
|
|
25
|
+
created_at: new Date().toISOString(),
|
|
26
|
+
last_updated: null,
|
|
27
|
+
services: []
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function loadState(stateFile) {
|
|
32
|
+
const filePath = stateFile || DEFAULT_STATE_FILE;
|
|
33
|
+
if (!fs.existsSync(filePath)) return defaultState();
|
|
34
|
+
try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
|
|
35
|
+
catch { return defaultState(); }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function saveState(state, stateFile) {
|
|
39
|
+
const filePath = stateFile || DEFAULT_STATE_FILE;
|
|
40
|
+
const dir = path.dirname(filePath);
|
|
41
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
42
|
+
state.last_updated = new Date().toISOString();
|
|
43
|
+
fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Define operational ownership for a service.
|
|
48
|
+
*
|
|
49
|
+
* @param {object} service - { name, service_owner, team, escalation_path[], oncall_model, support_hours, runbook_url?, sla_tier? }
|
|
50
|
+
* @param {object} [options]
|
|
51
|
+
* @returns {object}
|
|
52
|
+
*/
|
|
53
|
+
function defineOwnership(service, options = {}) {
|
|
54
|
+
if (!service || !service.name || !service.service_owner) {
|
|
55
|
+
return { success: false, error: 'name and service_owner are required' };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
59
|
+
const state = loadState(stateFile);
|
|
60
|
+
|
|
61
|
+
const svc = {
|
|
62
|
+
id: `OPS-${(state.services.length + 1).toString().padStart(3, '0')}`,
|
|
63
|
+
name: service.name,
|
|
64
|
+
service_owner: service.service_owner,
|
|
65
|
+
team: service.team || null,
|
|
66
|
+
escalation_path: service.escalation_path || [],
|
|
67
|
+
oncall_model: service.oncall_model || 'business-hours',
|
|
68
|
+
support_hours: service.support_hours || '9x5',
|
|
69
|
+
runbook_url: service.runbook_url || null,
|
|
70
|
+
sla_tier: service.sla_tier || 'silver',
|
|
71
|
+
defined_at: new Date().toISOString()
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Replace if already exists
|
|
75
|
+
const idx = state.services.findIndex(s => s.name === service.name);
|
|
76
|
+
if (idx >= 0) state.services[idx] = svc;
|
|
77
|
+
else state.services.push(svc);
|
|
78
|
+
|
|
79
|
+
saveState(state, stateFile);
|
|
80
|
+
|
|
81
|
+
return { success: true, service: svc };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check ownership completeness.
|
|
86
|
+
*
|
|
87
|
+
* @param {object} [options]
|
|
88
|
+
* @returns {object}
|
|
89
|
+
*/
|
|
90
|
+
function checkCompleteness(options = {}) {
|
|
91
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
92
|
+
const state = loadState(stateFile);
|
|
93
|
+
|
|
94
|
+
const incomplete = [];
|
|
95
|
+
for (const svc of state.services) {
|
|
96
|
+
const missing = [];
|
|
97
|
+
if (!svc.service_owner) missing.push('service_owner');
|
|
98
|
+
if (!svc.team) missing.push('team');
|
|
99
|
+
if (!svc.escalation_path || svc.escalation_path.length === 0) missing.push('escalation_path');
|
|
100
|
+
if (!svc.runbook_url) missing.push('runbook_url');
|
|
101
|
+
|
|
102
|
+
if (missing.length > 0) {
|
|
103
|
+
incomplete.push({ service: svc.name, missing });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
success: true,
|
|
109
|
+
total_services: state.services.length,
|
|
110
|
+
complete: state.services.length - incomplete.length,
|
|
111
|
+
incomplete: incomplete.length,
|
|
112
|
+
findings: incomplete,
|
|
113
|
+
all_complete: incomplete.length === 0
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Generate ops ownership report.
|
|
119
|
+
*
|
|
120
|
+
* @param {object} [options]
|
|
121
|
+
* @returns {object}
|
|
122
|
+
*/
|
|
123
|
+
function generateReport(options = {}) {
|
|
124
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
125
|
+
const state = loadState(stateFile);
|
|
126
|
+
|
|
127
|
+
const byTeam = {};
|
|
128
|
+
const byTier = {};
|
|
129
|
+
for (const svc of state.services) {
|
|
130
|
+
const team = svc.team || 'unassigned';
|
|
131
|
+
byTeam[team] = (byTeam[team] || 0) + 1;
|
|
132
|
+
byTier[svc.sla_tier] = (byTier[svc.sla_tier] || 0) + 1;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
success: true,
|
|
137
|
+
total_services: state.services.length,
|
|
138
|
+
by_team: byTeam,
|
|
139
|
+
by_tier: byTier,
|
|
140
|
+
services: state.services
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = {
|
|
145
|
+
defaultState,
|
|
146
|
+
loadState,
|
|
147
|
+
saveState,
|
|
148
|
+
defineOwnership,
|
|
149
|
+
checkCompleteness,
|
|
150
|
+
generateReport,
|
|
151
|
+
OWNERSHIP_FIELDS
|
|
152
|
+
};
|