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,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* telemetry-feedback.js — Production Telemetry Feedback Loop (Item 95)
|
|
3
|
+
*
|
|
4
|
+
* Feed real performance and reliability data back into
|
|
5
|
+
* future planning and architecture.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node bin/lib/telemetry-feedback.js ingest|analyze|report [options]
|
|
9
|
+
*
|
|
10
|
+
* State file: .jumpstart/state/telemetry-feedback.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', 'telemetry-feedback.json');
|
|
19
|
+
|
|
20
|
+
const METRIC_TYPES = ['latency', 'error-rate', 'throughput', 'availability', 'saturation', 'cost'];
|
|
21
|
+
|
|
22
|
+
function defaultState() {
|
|
23
|
+
return { version: '1.0.0', metrics: [], insights: [], last_updated: null };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function loadState(stateFile) {
|
|
27
|
+
const fp = stateFile || DEFAULT_STATE_FILE;
|
|
28
|
+
if (!fs.existsSync(fp)) return defaultState();
|
|
29
|
+
try { return JSON.parse(fs.readFileSync(fp, 'utf8')); }
|
|
30
|
+
catch { return defaultState(); }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function saveState(state, stateFile) {
|
|
34
|
+
const fp = stateFile || DEFAULT_STATE_FILE;
|
|
35
|
+
const dir = path.dirname(fp);
|
|
36
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
37
|
+
state.last_updated = new Date().toISOString();
|
|
38
|
+
fs.writeFileSync(fp, JSON.stringify(state, null, 2) + '\n', 'utf8');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function ingestMetric(name, type, value, options = {}) {
|
|
42
|
+
if (!name || !type) return { success: false, error: 'name and type are required' };
|
|
43
|
+
if (!METRIC_TYPES.includes(type)) {
|
|
44
|
+
return { success: false, error: `Unknown type: ${type}. Valid: ${METRIC_TYPES.join(', ')}` };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
48
|
+
const state = loadState(stateFile);
|
|
49
|
+
|
|
50
|
+
const metric = {
|
|
51
|
+
id: `TEL-${Date.now()}`,
|
|
52
|
+
name,
|
|
53
|
+
type,
|
|
54
|
+
value,
|
|
55
|
+
unit: options.unit || null,
|
|
56
|
+
service: options.service || null,
|
|
57
|
+
timestamp: new Date().toISOString()
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
state.metrics.push(metric);
|
|
61
|
+
saveState(state, stateFile);
|
|
62
|
+
|
|
63
|
+
return { success: true, metric };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function analyzeMetrics(options = {}) {
|
|
67
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
68
|
+
const state = loadState(stateFile);
|
|
69
|
+
|
|
70
|
+
const byType = {};
|
|
71
|
+
for (const m of state.metrics) {
|
|
72
|
+
if (!byType[m.type]) byType[m.type] = [];
|
|
73
|
+
byType[m.type].push(m.value);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const analysis = {};
|
|
77
|
+
for (const [type, values] of Object.entries(byType)) {
|
|
78
|
+
const nums = values.filter(v => typeof v === 'number');
|
|
79
|
+
if (nums.length > 0) {
|
|
80
|
+
analysis[type] = {
|
|
81
|
+
count: nums.length,
|
|
82
|
+
avg: Math.round((nums.reduce((a, b) => a + b, 0) / nums.length) * 100) / 100,
|
|
83
|
+
min: Math.min(...nums),
|
|
84
|
+
max: Math.max(...nums)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return { success: true, total_metrics: state.metrics.length, analysis };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function generateFeedbackReport(options = {}) {
|
|
93
|
+
const stateFile = options.stateFile || DEFAULT_STATE_FILE;
|
|
94
|
+
const state = loadState(stateFile);
|
|
95
|
+
const analysis = analyzeMetrics(options);
|
|
96
|
+
|
|
97
|
+
const recommendations = [];
|
|
98
|
+
if (analysis.analysis.latency && analysis.analysis.latency.avg > 500) {
|
|
99
|
+
recommendations.push('High average latency detected — consider caching or query optimization');
|
|
100
|
+
}
|
|
101
|
+
if (analysis.analysis['error-rate'] && analysis.analysis['error-rate'].avg > 5) {
|
|
102
|
+
recommendations.push('Elevated error rate — review error handling and resilience patterns');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
success: true,
|
|
107
|
+
total_metrics: state.metrics.length,
|
|
108
|
+
total_insights: state.insights.length,
|
|
109
|
+
analysis: analysis.analysis,
|
|
110
|
+
recommendations
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
ingestMetric, analyzeMetrics, generateFeedbackReport,
|
|
116
|
+
loadState, saveState, defaultState,
|
|
117
|
+
METRIC_TYPES
|
|
118
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* test-generator.js — Test Generation Tied to Acceptance Criteria (Item 44)
|
|
3
|
+
*
|
|
4
|
+
* Auto-generate unit, integration, API, UI, and contract tests
|
|
5
|
+
* from requirements.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node bin/lib/test-generator.js generate|coverage [options]
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const TEST_TYPES = ['unit', 'integration', 'api', 'ui', 'contract', 'e2e'];
|
|
17
|
+
|
|
18
|
+
const TEST_FRAMEWORKS = {
|
|
19
|
+
javascript: { framework: 'vitest', extension: '.test.js', import: "import { describe, it, expect } from 'vitest';" },
|
|
20
|
+
typescript: { framework: 'vitest', extension: '.test.ts', import: "import { describe, it, expect } from 'vitest';" },
|
|
21
|
+
python: { framework: 'pytest', extension: '_test.py', import: 'import pytest' }
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Extract acceptance criteria from PRD content.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} content - PRD markdown content.
|
|
28
|
+
* @returns {object[]}
|
|
29
|
+
*/
|
|
30
|
+
function extractCriteria(content) {
|
|
31
|
+
const criteria = [];
|
|
32
|
+
const lines = content.split('\n');
|
|
33
|
+
let currentStory = null;
|
|
34
|
+
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
const storyMatch = line.match(/\*\*(E\d+-S\d+)\*\*/);
|
|
37
|
+
if (storyMatch) currentStory = storyMatch[1];
|
|
38
|
+
|
|
39
|
+
const givenMatch = line.match(/^\s*[-*]\s*(Given\s+.+)/i);
|
|
40
|
+
const whenMatch = line.match(/^\s*[-*]\s*(When\s+.+)/i);
|
|
41
|
+
const thenMatch = line.match(/^\s*[-*]\s*(Then\s+.+)/i);
|
|
42
|
+
const acMatch = line.match(/^\s*[-*]\s*AC\d*:\s*(.+)/i);
|
|
43
|
+
|
|
44
|
+
if (givenMatch || whenMatch || thenMatch || acMatch) {
|
|
45
|
+
criteria.push({
|
|
46
|
+
story: currentStory,
|
|
47
|
+
criterion: (givenMatch || whenMatch || thenMatch || acMatch)[1].trim(),
|
|
48
|
+
type: givenMatch ? 'given' : whenMatch ? 'when' : thenMatch ? 'then' : 'acceptance'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return criteria;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Generate test stubs from acceptance criteria.
|
|
58
|
+
*
|
|
59
|
+
* @param {object[]} criteria - Extracted criteria.
|
|
60
|
+
* @param {object} [options]
|
|
61
|
+
* @returns {object}
|
|
62
|
+
*/
|
|
63
|
+
function generateTestStubs(criteria, options = {}) {
|
|
64
|
+
const language = options.language || 'javascript';
|
|
65
|
+
const fwConfig = TEST_FRAMEWORKS[language];
|
|
66
|
+
if (!fwConfig) {
|
|
67
|
+
return { success: false, error: `Unsupported language: ${language}` };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const byStory = {};
|
|
71
|
+
for (const c of criteria) {
|
|
72
|
+
const story = c.story || 'general';
|
|
73
|
+
if (!byStory[story]) byStory[story] = [];
|
|
74
|
+
byStory[story].push(c);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const testFiles = [];
|
|
78
|
+
for (const [story, storyCriteria] of Object.entries(byStory)) {
|
|
79
|
+
const fileName = `${story.toLowerCase().replace(/[^a-z0-9]/g, '-')}${fwConfig.extension}`;
|
|
80
|
+
const tests = storyCriteria.map(c => {
|
|
81
|
+
const testName = c.criterion.replace(/'/g, "\\'").substring(0, 100);
|
|
82
|
+
return ` it('${testName}', () => {\n // TODO: Implement test for ${c.type}\n expect(true).toBe(true);\n });`;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const content = `${fwConfig.import}\n\ndescribe('${story}', () => {\n${tests.join('\n\n')}\n});\n`;
|
|
86
|
+
testFiles.push({ fileName, content, story, test_count: storyCriteria.length });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
success: true,
|
|
91
|
+
total_criteria: criteria.length,
|
|
92
|
+
test_files: testFiles.length,
|
|
93
|
+
files: testFiles,
|
|
94
|
+
framework: fwConfig.framework
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check test coverage against acceptance criteria.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} root - Project root.
|
|
102
|
+
* @param {object} [options]
|
|
103
|
+
* @returns {object}
|
|
104
|
+
*/
|
|
105
|
+
function checkCoverage(root, options = {}) {
|
|
106
|
+
const prdFile = path.join(root, 'specs', 'prd.md');
|
|
107
|
+
if (!fs.existsSync(prdFile)) {
|
|
108
|
+
return { success: false, error: 'PRD not found at specs/prd.md' };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const prdContent = fs.readFileSync(prdFile, 'utf8');
|
|
112
|
+
const criteria = extractCriteria(prdContent);
|
|
113
|
+
|
|
114
|
+
// Check test directory for matching tests
|
|
115
|
+
const testDir = path.join(root, 'tests');
|
|
116
|
+
let testContent = '';
|
|
117
|
+
if (fs.existsSync(testDir)) {
|
|
118
|
+
for (const entry of fs.readdirSync(testDir)) {
|
|
119
|
+
if (entry.endsWith('.test.js') || entry.endsWith('.test.ts')) {
|
|
120
|
+
try { testContent += fs.readFileSync(path.join(testDir, entry), 'utf8') + '\n'; }
|
|
121
|
+
catch { /* skip */ }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const covered = criteria.filter(c => {
|
|
127
|
+
const terms = c.criterion.toLowerCase().split(/\s+/).filter(t => t.length > 3);
|
|
128
|
+
return terms.some(t => testContent.toLowerCase().includes(t));
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
success: true,
|
|
133
|
+
total_criteria: criteria.length,
|
|
134
|
+
covered: covered.length,
|
|
135
|
+
coverage: criteria.length > 0 ? Math.round((covered.length / criteria.length) * 100) : 0,
|
|
136
|
+
uncovered: criteria.filter(c => !covered.includes(c))
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = {
|
|
141
|
+
extractCriteria,
|
|
142
|
+
generateTestStubs,
|
|
143
|
+
checkCoverage,
|
|
144
|
+
TEST_TYPES,
|
|
145
|
+
TEST_FRAMEWORKS
|
|
146
|
+
};
|
package/bin/lib/timeline.js
CHANGED
|
@@ -36,6 +36,7 @@ const EVENT_TYPES = [
|
|
|
36
36
|
'approval', 'rejection',
|
|
37
37
|
'subagent_invoked', 'subagent_completed',
|
|
38
38
|
'llm_turn_start', 'llm_turn_end',
|
|
39
|
+
'prompt_logged',
|
|
39
40
|
'research_query', 'checkpoint_created', 'rewind',
|
|
40
41
|
'handoff', 'usage_logged', 'custom'
|
|
41
42
|
];
|
|
@@ -780,7 +781,7 @@ function _shouldCapture(eventType, capture) {
|
|
|
780
781
|
case 'research_query':
|
|
781
782
|
return capture.research;
|
|
782
783
|
default:
|
|
783
|
-
return true; // Always capture: phase_start, phase_end, handoff, checkpoint, rewind, usage_logged, custom
|
|
784
|
+
return true; // Always capture: phase_start, phase_end, prompt_logged, handoff, checkpoint, rewind, usage_logged, custom
|
|
784
785
|
}
|
|
785
786
|
}
|
|
786
787
|
|
package/bin/lib/tool-bridge.js
CHANGED
|
@@ -253,6 +253,165 @@ function createToolBridge(options = {}) {
|
|
|
253
253
|
duration_ms: args.duration_ms || null
|
|
254
254
|
});
|
|
255
255
|
return { success: !!evt, event_id: evt ? evt.id : null };
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Log token usage and cost to .jumpstart/usage-log.json.
|
|
260
|
+
*/
|
|
261
|
+
async log_usage(args) {
|
|
262
|
+
try {
|
|
263
|
+
const { logUsage } = await import('./usage.js');
|
|
264
|
+
const logPath = path.join(workspaceDir, '.jumpstart', 'usage-log.json');
|
|
265
|
+
const entry = {
|
|
266
|
+
phase: args.phase || 'unknown',
|
|
267
|
+
agent: args.agent || 'unknown',
|
|
268
|
+
action: args.action || 'unknown',
|
|
269
|
+
estimated_tokens: args.estimated_tokens || 0,
|
|
270
|
+
estimated_cost_usd: args.estimated_cost_usd || (args.estimated_tokens || 0) * 0.000002,
|
|
271
|
+
model: args.model || null,
|
|
272
|
+
metadata: args.metadata || null
|
|
273
|
+
};
|
|
274
|
+
const log = logUsage(logPath, entry);
|
|
275
|
+
return { success: true, total_tokens: log.total_tokens, total_entries: log.entries.length };
|
|
276
|
+
} catch (err) {
|
|
277
|
+
return { success: false, error: err.message };
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
// ── Item-Tagged Feature Tool Handlers ──────────────────────────────────
|
|
282
|
+
|
|
283
|
+
async run_revert(args) {
|
|
284
|
+
try {
|
|
285
|
+
const { revertArtifact } = await import('./revert.js');
|
|
286
|
+
return revertArtifact({ artifact: args.artifact, reason: args.reason, archive_dir: args.archive_dir });
|
|
287
|
+
} catch (err) {
|
|
288
|
+
return { success: false, error: err.message };
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
async run_adr_index(args) {
|
|
293
|
+
try {
|
|
294
|
+
const { buildIndex, searchIndex } = await import('./adr-index.js');
|
|
295
|
+
const root = args.root || workspaceDir;
|
|
296
|
+
if (args.action === 'search') {
|
|
297
|
+
const index = buildIndex(root);
|
|
298
|
+
return searchIndex(index, args.query || '', { tag: args.tag });
|
|
299
|
+
}
|
|
300
|
+
return buildIndex(root);
|
|
301
|
+
} catch (err) {
|
|
302
|
+
return { success: false, error: err.message };
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
async run_complexity(args) {
|
|
307
|
+
try {
|
|
308
|
+
const { calculateComplexity } = await import('./complexity.js');
|
|
309
|
+
return calculateComplexity({ description: args.description || '', root: args.root || workspaceDir });
|
|
310
|
+
} catch (err) {
|
|
311
|
+
return { success: false, error: err.message };
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
async run_crossref(args) {
|
|
316
|
+
try {
|
|
317
|
+
const { validateCrossRefs } = await import('./crossref.js');
|
|
318
|
+
return validateCrossRefs(args.specs_dir || 'specs', args.root || workspaceDir);
|
|
319
|
+
} catch (err) {
|
|
320
|
+
return { success: false, error: err.message };
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
async run_init(args) {
|
|
325
|
+
try {
|
|
326
|
+
const { generateInitConfig } = await import('./init.js');
|
|
327
|
+
return generateInitConfig({ skill_level: args.skill_level || 'intermediate', project_type: args.project_type || 'greenfield' });
|
|
328
|
+
} catch (err) {
|
|
329
|
+
return { success: false, error: err.message };
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
async run_lock(args) {
|
|
334
|
+
try {
|
|
335
|
+
const { acquireLock, releaseLock, lockStatus, listLocks } = await import('./locks.js');
|
|
336
|
+
if (args.action === 'acquire') return acquireLock(args.file, args.agent || 'headless');
|
|
337
|
+
if (args.action === 'release') return releaseLock(args.file, args.agent || 'headless');
|
|
338
|
+
if (args.action === 'status') return lockStatus(args.file);
|
|
339
|
+
return listLocks();
|
|
340
|
+
} catch (err) {
|
|
341
|
+
return { success: false, error: err.message };
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
async run_timestamp(args) {
|
|
346
|
+
try {
|
|
347
|
+
const { now, validate, audit } = await import('./timestamps.js');
|
|
348
|
+
if (args.action === 'validate') return validate(args.value);
|
|
349
|
+
if (args.action === 'audit') return audit(args.file);
|
|
350
|
+
return { timestamp: now() };
|
|
351
|
+
} catch (err) {
|
|
352
|
+
return { success: false, error: err.message };
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
async run_scan(args) {
|
|
357
|
+
try {
|
|
358
|
+
const { scan } = await import('./scanner.js');
|
|
359
|
+
return scan({ root: args.root || workspaceDir, ignore: args.ignore });
|
|
360
|
+
} catch (err) {
|
|
361
|
+
return { success: false, error: err.message };
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
|
|
365
|
+
// ── Quality Gate Tool Handlers ──────────────────────────────────────────
|
|
366
|
+
|
|
367
|
+
async run_secret_scan(args) {
|
|
368
|
+
try {
|
|
369
|
+
const { runSecretScan } = require('./secret-scanner.js');
|
|
370
|
+
return runSecretScan({
|
|
371
|
+
files: args.files || [],
|
|
372
|
+
root: args.root || workspaceDir,
|
|
373
|
+
config: args.config || {}
|
|
374
|
+
});
|
|
375
|
+
} catch (err) {
|
|
376
|
+
return { error: err.message, pass: false };
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
async run_type_check(args) {
|
|
381
|
+
try {
|
|
382
|
+
const { runTypeCheck } = require('./type-checker.js');
|
|
383
|
+
return runTypeCheck({
|
|
384
|
+
files: args.files || [],
|
|
385
|
+
root: args.root || workspaceDir,
|
|
386
|
+
config: args.config || {}
|
|
387
|
+
});
|
|
388
|
+
} catch (err) {
|
|
389
|
+
return { error: err.message, pass: false };
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
|
|
393
|
+
async run_smoke_test(args) {
|
|
394
|
+
try {
|
|
395
|
+
const { runSmokeTest } = require('./smoke-tester.js');
|
|
396
|
+
return await runSmokeTest({
|
|
397
|
+
root: args.root || workspaceDir,
|
|
398
|
+
config: args.config || {}
|
|
399
|
+
});
|
|
400
|
+
} catch (err) {
|
|
401
|
+
return { error: err.message, pass: false };
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
|
|
405
|
+
async run_uat_coverage(args) {
|
|
406
|
+
try {
|
|
407
|
+
const { computeUATCoverage } = require('./uat-coverage.js');
|
|
408
|
+
return computeUATCoverage(
|
|
409
|
+
args.prd_path,
|
|
410
|
+
args.test_dir
|
|
411
|
+
);
|
|
412
|
+
} catch (err) {
|
|
413
|
+
return { error: err.message, pass: false };
|
|
414
|
+
}
|
|
256
415
|
}
|
|
257
416
|
};
|
|
258
417
|
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tool-guardrails.js — Tool Execution Guardrails (Item 58)
|
|
3
|
+
*
|
|
4
|
+
* Validate risky file operations, deletions, schema changes,
|
|
5
|
+
* and wide-scope edits before execution.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node bin/lib/tool-guardrails.js check|policy|report [options]
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const RISK_RULES = [
|
|
17
|
+
{ id: 'delete-protection', pattern: /^(?:rm|del|remove)\s+/i, risk: 'high', description: 'File deletion operation' },
|
|
18
|
+
{ id: 'recursive-delete', pattern: /rm\s+-rf?\s/i, risk: 'critical', description: 'Recursive deletion' },
|
|
19
|
+
{ id: 'schema-change', pattern: /(?:ALTER|DROP|TRUNCATE)\s+(?:TABLE|DATABASE|SCHEMA)/i, risk: 'high', description: 'Database schema modification' },
|
|
20
|
+
{ id: 'config-write', pattern: /(?:\.env|config|secrets?)\s*$/i, risk: 'high', description: 'Configuration file modification' },
|
|
21
|
+
{ id: 'wide-glob', pattern: /\*\*\/\*|\*\.\*/i, risk: 'medium', description: 'Wide glob pattern' },
|
|
22
|
+
{ id: 'sudo-usage', pattern: /\bsudo\b/i, risk: 'critical', description: 'Elevated privilege usage' },
|
|
23
|
+
{ id: 'network-call', pattern: /\b(?:curl|wget|fetch)\s+http/i, risk: 'medium', description: 'External network call' },
|
|
24
|
+
{ id: 'git-force', pattern: /git\s+(?:push\s+--force|reset\s+--hard)/i, risk: 'high', description: 'Force git operation' }
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const PROTECTED_PATHS = [
|
|
28
|
+
'.env', '.env.local', '.env.production',
|
|
29
|
+
'.git/', 'node_modules/',
|
|
30
|
+
'package-lock.json', 'yarn.lock',
|
|
31
|
+
'.jumpstart/state/'
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check a command or operation for guardrail violations.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} operation - Command or file path.
|
|
38
|
+
* @param {object} [options]
|
|
39
|
+
* @returns {object}
|
|
40
|
+
*/
|
|
41
|
+
function checkOperation(operation, options = {}) {
|
|
42
|
+
if (!operation) return { success: false, error: 'operation is required' };
|
|
43
|
+
|
|
44
|
+
const violations = [];
|
|
45
|
+
|
|
46
|
+
// Check against risk rules
|
|
47
|
+
for (const rule of RISK_RULES) {
|
|
48
|
+
if (rule.pattern.test(operation)) {
|
|
49
|
+
violations.push({
|
|
50
|
+
rule_id: rule.id,
|
|
51
|
+
risk: rule.risk,
|
|
52
|
+
description: rule.description,
|
|
53
|
+
matched: operation.substring(0, 100)
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check against protected paths
|
|
59
|
+
for (const pp of PROTECTED_PATHS) {
|
|
60
|
+
if (operation.includes(pp)) {
|
|
61
|
+
violations.push({
|
|
62
|
+
rule_id: 'protected-path',
|
|
63
|
+
risk: 'high',
|
|
64
|
+
description: `Operation targets protected path: ${pp}`,
|
|
65
|
+
matched: pp
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const maxRisk = violations.reduce((max, v) => {
|
|
71
|
+
const riskOrder = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
72
|
+
return riskOrder[v.risk] > riskOrder[max] ? v.risk : max;
|
|
73
|
+
}, 'low');
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
success: true,
|
|
77
|
+
operation: operation.substring(0, 200),
|
|
78
|
+
allowed: violations.filter(v => v.risk === 'critical').length === 0,
|
|
79
|
+
requires_approval: violations.some(v => v.risk === 'high' || v.risk === 'critical'),
|
|
80
|
+
risk_level: violations.length > 0 ? maxRisk : 'none',
|
|
81
|
+
violations,
|
|
82
|
+
total_violations: violations.length
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Validate a file operation (create/edit/delete).
|
|
88
|
+
*
|
|
89
|
+
* @param {string} action - 'create' | 'edit' | 'delete'
|
|
90
|
+
* @param {string} filePath
|
|
91
|
+
* @param {object} [options]
|
|
92
|
+
* @returns {object}
|
|
93
|
+
*/
|
|
94
|
+
function validateFileOperation(action, filePath, options = {}) {
|
|
95
|
+
const warnings = [];
|
|
96
|
+
|
|
97
|
+
if (action === 'delete') {
|
|
98
|
+
warnings.push({ level: 'high', message: `Deleting file: ${filePath}` });
|
|
99
|
+
|
|
100
|
+
for (const pp of PROTECTED_PATHS) {
|
|
101
|
+
if (filePath.includes(pp)) {
|
|
102
|
+
return {
|
|
103
|
+
success: true,
|
|
104
|
+
allowed: false,
|
|
105
|
+
reason: `Cannot delete protected path: ${pp}`,
|
|
106
|
+
warnings
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (action === 'edit') {
|
|
113
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
114
|
+
if (['.env', '.pem', '.key'].includes(ext)) {
|
|
115
|
+
warnings.push({ level: 'high', message: 'Editing sensitive file type' });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const linesChanged = options.lines_changed || 0;
|
|
120
|
+
if (linesChanged > 100) {
|
|
121
|
+
warnings.push({ level: 'medium', message: `Large edit: ${linesChanged} lines changed` });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
success: true,
|
|
126
|
+
allowed: true,
|
|
127
|
+
action,
|
|
128
|
+
file: filePath,
|
|
129
|
+
warnings,
|
|
130
|
+
requires_review: warnings.some(w => w.level === 'high')
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
module.exports = {
|
|
135
|
+
checkOperation,
|
|
136
|
+
validateFileOperation,
|
|
137
|
+
RISK_RULES,
|
|
138
|
+
PROTECTED_PATHS
|
|
139
|
+
};
|