ralph-hero-mcp-server 2.4.17 → 2.4.22
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.
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named filter profiles for Ralph agent roles.
|
|
3
|
+
*
|
|
4
|
+
* Maps profile names (e.g., "analyst-triage", "builder-active") to sets of
|
|
5
|
+
* filter parameters for list_issues and list_project_items. Agents use profiles
|
|
6
|
+
* instead of hardcoding individual filter params.
|
|
7
|
+
*/
|
|
8
|
+
// --- Profile Registry ---
|
|
9
|
+
/**
|
|
10
|
+
* Named filter profiles mapping to concrete filter parameter objects.
|
|
11
|
+
* Profiles use kebab-case with role prefix: role-purpose.
|
|
12
|
+
*/
|
|
13
|
+
export const FILTER_PROFILES = {
|
|
14
|
+
"analyst-triage": {
|
|
15
|
+
workflowState: "Backlog",
|
|
16
|
+
// TODO: add `no: "estimate"` when GH-141 (has/no presence filters) lands
|
|
17
|
+
},
|
|
18
|
+
"analyst-research": {
|
|
19
|
+
workflowState: "Research Needed",
|
|
20
|
+
},
|
|
21
|
+
"builder-active": {
|
|
22
|
+
workflowState: "In Progress",
|
|
23
|
+
},
|
|
24
|
+
"builder-planned": {
|
|
25
|
+
workflowState: "Plan in Review",
|
|
26
|
+
},
|
|
27
|
+
"validator-review": {
|
|
28
|
+
workflowState: "Plan in Review",
|
|
29
|
+
// TODO: multi-value workflowState for "Plan in Review" OR "In Review" when supported
|
|
30
|
+
},
|
|
31
|
+
"integrator-merge": {
|
|
32
|
+
workflowState: "In Review",
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Valid profile names, derived from the registry keys.
|
|
37
|
+
*/
|
|
38
|
+
export const VALID_PROFILE_NAMES = Object.keys(FILTER_PROFILES);
|
|
39
|
+
// --- Public API ---
|
|
40
|
+
/**
|
|
41
|
+
* Expand a named profile into its filter parameters.
|
|
42
|
+
*
|
|
43
|
+
* Returns a shallow copy of the profile's filter params (safe to mutate).
|
|
44
|
+
* Throws if the profile name is not recognized.
|
|
45
|
+
*/
|
|
46
|
+
export function expandProfile(name) {
|
|
47
|
+
const profile = FILTER_PROFILES[name];
|
|
48
|
+
if (!profile) {
|
|
49
|
+
throw new Error(`Unknown filter profile "${name}". ` +
|
|
50
|
+
`Valid profiles: ${VALID_PROFILE_NAMES.join(", ")}. ` +
|
|
51
|
+
`Recovery: retry with one of the valid profile names listed above.`);
|
|
52
|
+
}
|
|
53
|
+
return { ...profile };
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=filter-profiles.js.map
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Velocity metrics, risk scoring, and auto-status determination — pure functions.
|
|
3
|
+
*
|
|
4
|
+
* All functions are side-effect-free: dashboard data in, metrics out.
|
|
5
|
+
* Designed to complement lib/dashboard.ts without modifying it.
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_METRICS_CONFIG = {
|
|
8
|
+
velocityWindowDays: 7,
|
|
9
|
+
atRiskThreshold: 2,
|
|
10
|
+
offTrackThreshold: 6,
|
|
11
|
+
severityWeights: { critical: 3, warning: 1, info: 0 },
|
|
12
|
+
};
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// calculateVelocity
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
/**
|
|
17
|
+
* Count items that moved to Done within a time window.
|
|
18
|
+
*
|
|
19
|
+
* Uses closedAt if available, otherwise falls back to updatedAt.
|
|
20
|
+
* Only counts items whose workflowState is "Done".
|
|
21
|
+
*/
|
|
22
|
+
export function calculateVelocity(items, windowDays, now) {
|
|
23
|
+
const windowMs = windowDays * 24 * 60 * 60 * 1000;
|
|
24
|
+
return items.filter((item) => {
|
|
25
|
+
if (item.workflowState !== "Done")
|
|
26
|
+
return false;
|
|
27
|
+
const ts = item.closedAt ?? item.updatedAt;
|
|
28
|
+
return now - new Date(ts).getTime() <= windowMs;
|
|
29
|
+
}).length;
|
|
30
|
+
}
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// calculateRiskScore
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
/**
|
|
35
|
+
* Aggregate health warnings into a numeric risk score.
|
|
36
|
+
*
|
|
37
|
+
* Each warning contributes its severity weight. Unknown severities
|
|
38
|
+
* default to 0.
|
|
39
|
+
*/
|
|
40
|
+
export function calculateRiskScore(warnings, weights) {
|
|
41
|
+
return warnings.reduce((score, w) => score + (weights[w.severity] ?? 0), 0);
|
|
42
|
+
}
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// determineStatus
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
/**
|
|
47
|
+
* Map a risk score to a project health status using configurable thresholds.
|
|
48
|
+
*/
|
|
49
|
+
export function determineStatus(riskScore, config) {
|
|
50
|
+
if (riskScore >= config.offTrackThreshold)
|
|
51
|
+
return "OFF_TRACK";
|
|
52
|
+
if (riskScore >= config.atRiskThreshold)
|
|
53
|
+
return "AT_RISK";
|
|
54
|
+
return "ON_TRACK";
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// extractHighlights
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
/**
|
|
60
|
+
* Extract recently completed and newly added items from dashboard data.
|
|
61
|
+
*
|
|
62
|
+
* - recentlyCompleted: items in the "Done" phase (already time-filtered
|
|
63
|
+
* by aggregateByPhase)
|
|
64
|
+
* - newlyAdded: items in "Backlog" whose ageHours is within the window
|
|
65
|
+
* (approximation since DashboardItem lacks createdAt)
|
|
66
|
+
*/
|
|
67
|
+
export function extractHighlights(data, windowDays, now) {
|
|
68
|
+
void now; // reserved for future createdAt-based filtering
|
|
69
|
+
const donePhase = data.phases.find((p) => p.state === "Done");
|
|
70
|
+
const recentlyCompleted = (donePhase?.issues ?? []).map((i) => ({
|
|
71
|
+
number: i.number,
|
|
72
|
+
title: i.title,
|
|
73
|
+
}));
|
|
74
|
+
const backlogPhase = data.phases.find((p) => p.state === "Backlog");
|
|
75
|
+
const maxAgeHours = windowDays * 24;
|
|
76
|
+
const newlyAdded = (backlogPhase?.issues ?? [])
|
|
77
|
+
.filter((i) => i.ageHours < maxAgeHours)
|
|
78
|
+
.map((i) => ({
|
|
79
|
+
number: i.number,
|
|
80
|
+
title: i.title,
|
|
81
|
+
}));
|
|
82
|
+
return { recentlyCompleted, newlyAdded };
|
|
83
|
+
}
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// calculateMetrics
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
/**
|
|
88
|
+
* Convenience orchestrator: compute all metrics from raw items and
|
|
89
|
+
* dashboard data in one call.
|
|
90
|
+
*/
|
|
91
|
+
export function calculateMetrics(items, data, config = DEFAULT_METRICS_CONFIG, now = Date.now()) {
|
|
92
|
+
const velocity = calculateVelocity(items, config.velocityWindowDays, now);
|
|
93
|
+
const riskScore = calculateRiskScore(data.health.warnings, config.severityWeights);
|
|
94
|
+
const status = determineStatus(riskScore, config);
|
|
95
|
+
const highlights = extractHighlights(data, config.velocityWindowDays, now);
|
|
96
|
+
return { velocity, riskScore, status, highlights };
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -9,6 +9,7 @@ import { z } from "zod";
|
|
|
9
9
|
import { paginateConnection } from "../lib/pagination.js";
|
|
10
10
|
import { buildDashboard, formatMarkdown, formatAscii, DEFAULT_HEALTH_CONFIG, } from "../lib/dashboard.js";
|
|
11
11
|
import { toolSuccess, toolError, resolveProjectOwner } from "../types.js";
|
|
12
|
+
import { calculateMetrics, DEFAULT_METRICS_CONFIG, } from "../lib/metrics.js";
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
13
14
|
// Helper: Ensure field option cache is populated
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
@@ -176,6 +177,26 @@ export function registerDashboardTools(server, client, fieldCache) {
|
|
|
176
177
|
.optional()
|
|
177
178
|
.default(10)
|
|
178
179
|
.describe("Max issues to list per phase (default: 10)"),
|
|
180
|
+
includeMetrics: z
|
|
181
|
+
.boolean()
|
|
182
|
+
.optional()
|
|
183
|
+
.default(false)
|
|
184
|
+
.describe("Include velocity metrics, risk score, and auto-status (default: false)"),
|
|
185
|
+
velocityWindowDays: z
|
|
186
|
+
.number()
|
|
187
|
+
.optional()
|
|
188
|
+
.default(7)
|
|
189
|
+
.describe("Days to look back for velocity calculation (default: 7)"),
|
|
190
|
+
atRiskThreshold: z
|
|
191
|
+
.number()
|
|
192
|
+
.optional()
|
|
193
|
+
.default(2)
|
|
194
|
+
.describe("Risk score threshold for AT_RISK status (default: 2)"),
|
|
195
|
+
offTrackThreshold: z
|
|
196
|
+
.number()
|
|
197
|
+
.optional()
|
|
198
|
+
.default(6)
|
|
199
|
+
.describe("Risk score threshold for OFF_TRACK status (default: 6)"),
|
|
179
200
|
}, async (args) => {
|
|
180
201
|
try {
|
|
181
202
|
const owner = args.owner || resolveProjectOwner(client.config);
|
|
@@ -215,6 +236,17 @@ export function registerDashboardTools(server, client, fieldCache) {
|
|
|
215
236
|
for (const phase of dashboard.phases) {
|
|
216
237
|
phase.issues = phase.issues.slice(0, issuesPerPhase);
|
|
217
238
|
}
|
|
239
|
+
// Compute metrics if requested
|
|
240
|
+
let metrics;
|
|
241
|
+
if (args.includeMetrics) {
|
|
242
|
+
const metricsConfig = {
|
|
243
|
+
...DEFAULT_METRICS_CONFIG,
|
|
244
|
+
velocityWindowDays: args.velocityWindowDays ?? 7,
|
|
245
|
+
atRiskThreshold: args.atRiskThreshold ?? 2,
|
|
246
|
+
offTrackThreshold: args.offTrackThreshold ?? 6,
|
|
247
|
+
};
|
|
248
|
+
metrics = calculateMetrics(dashboardItems, dashboard, metricsConfig);
|
|
249
|
+
}
|
|
218
250
|
// Format output
|
|
219
251
|
const format = args.format ?? "json";
|
|
220
252
|
let formatted;
|
|
@@ -227,6 +259,7 @@ export function registerDashboardTools(server, client, fieldCache) {
|
|
|
227
259
|
return toolSuccess({
|
|
228
260
|
...dashboard,
|
|
229
261
|
...(formatted !== undefined ? { formatted } : {}),
|
|
262
|
+
...(metrics !== undefined ? { metrics } : {}),
|
|
230
263
|
});
|
|
231
264
|
}
|
|
232
265
|
catch (error) {
|
|
@@ -11,6 +11,7 @@ import { detectPipelinePosition, } from "../lib/pipeline-detection.js";
|
|
|
11
11
|
import { isValidState, VALID_STATES, LOCK_STATES, } from "../lib/workflow-states.js";
|
|
12
12
|
import { resolveState } from "../lib/state-resolution.js";
|
|
13
13
|
import { parseDateMath } from "../lib/date-math.js";
|
|
14
|
+
import { expandProfile } from "../lib/filter-profiles.js";
|
|
14
15
|
import { toolSuccess, toolError } from "../types.js";
|
|
15
16
|
import { ensureFieldCache, resolveIssueNodeId, resolveProjectItemId, updateProjectItemField, getCurrentFieldValue, resolveConfig, resolveFullConfig, syncStatusField, } from "../lib/helpers.js";
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
@@ -29,6 +30,11 @@ export function registerIssueTools(server, client, fieldCache) {
|
|
|
29
30
|
.string()
|
|
30
31
|
.optional()
|
|
31
32
|
.describe("Repository name. Defaults to GITHUB_REPO env var"),
|
|
33
|
+
profile: z
|
|
34
|
+
.string()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("Named filter profile (e.g., 'analyst-triage', 'builder-active'). " +
|
|
37
|
+
"Profile filters are defaults; explicit params override them."),
|
|
32
38
|
workflowState: z
|
|
33
39
|
.string()
|
|
34
40
|
.optional()
|
|
@@ -72,6 +78,15 @@ export function registerIssueTools(server, client, fieldCache) {
|
|
|
72
78
|
.describe("Max items to return (default 50)"),
|
|
73
79
|
}, async (args) => {
|
|
74
80
|
try {
|
|
81
|
+
// Expand profile into filter defaults (explicit args override)
|
|
82
|
+
if (args.profile) {
|
|
83
|
+
const profileFilters = expandProfile(args.profile);
|
|
84
|
+
for (const [key, value] of Object.entries(profileFilters)) {
|
|
85
|
+
if (args[key] === undefined) {
|
|
86
|
+
args[key] = value;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
75
90
|
const { owner, repo, projectNumber, projectOwner } = resolveFullConfig(client, args);
|
|
76
91
|
// Ensure field cache is populated
|
|
77
92
|
await ensureFieldCache(client, fieldCache, projectOwner, projectNumber);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { paginateConnection } from "../lib/pagination.js";
|
|
9
9
|
import { parseDateMath } from "../lib/date-math.js";
|
|
10
|
+
import { expandProfile } from "../lib/filter-profiles.js";
|
|
10
11
|
import { toolSuccess, toolError } from "../types.js";
|
|
11
12
|
import { resolveProjectOwner } from "../types.js";
|
|
12
13
|
const WORKFLOW_STATE_OPTIONS = [
|
|
@@ -240,6 +241,11 @@ export function registerProjectTools(server, client, fieldCache) {
|
|
|
240
241
|
.number()
|
|
241
242
|
.optional()
|
|
242
243
|
.describe("Project number. Defaults to RALPH_GH_PROJECT_NUMBER env var"),
|
|
244
|
+
profile: z
|
|
245
|
+
.string()
|
|
246
|
+
.optional()
|
|
247
|
+
.describe("Named filter profile (e.g., 'analyst-triage', 'builder-active'). " +
|
|
248
|
+
"Profile filters are defaults; explicit params override them."),
|
|
243
249
|
workflowState: z
|
|
244
250
|
.string()
|
|
245
251
|
.optional()
|
|
@@ -271,6 +277,15 @@ export function registerProjectTools(server, client, fieldCache) {
|
|
|
271
277
|
.describe("Max items to return (default 50)"),
|
|
272
278
|
}, async (args) => {
|
|
273
279
|
try {
|
|
280
|
+
// Expand profile into filter defaults (explicit args override)
|
|
281
|
+
if (args.profile) {
|
|
282
|
+
const profileFilters = expandProfile(args.profile);
|
|
283
|
+
for (const [key, value] of Object.entries(profileFilters)) {
|
|
284
|
+
if (args[key] === undefined) {
|
|
285
|
+
args[key] = value;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
274
289
|
const owner = args.owner || resolveProjectOwner(client.config);
|
|
275
290
|
const projectNumber = args.number || client.config.projectNumber;
|
|
276
291
|
if (!owner) {
|