@elixium.ai/mcp-server 0.2.2 → 0.3.1
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/dist/index.js +249 -17
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -227,6 +227,105 @@ const getTeamProfile = async () => {
|
|
|
227
227
|
const config = await fetchFeatureConfig();
|
|
228
228
|
return config.teamProfile;
|
|
229
229
|
};
|
|
230
|
+
const formatTeamContext = (config) => {
|
|
231
|
+
const features = config.features;
|
|
232
|
+
const tp = config.teamProfile;
|
|
233
|
+
const bd = config.branchingDefaults;
|
|
234
|
+
const infra = config.infrastructureProfile;
|
|
235
|
+
return `
|
|
236
|
+
## Team Context
|
|
237
|
+
|
|
238
|
+
### Enabled Features
|
|
239
|
+
${features.balancedTeam ? "✅" : "❌"} Balanced Team
|
|
240
|
+
${features.learningLoop ? "✅" : "❌"} Learning Loop
|
|
241
|
+
${features.tddWorkflow ? "✅" : "❌"} TDD Workflow
|
|
242
|
+
${features.aiTools ? "✅" : "❌"} AI Tools
|
|
243
|
+
|
|
244
|
+
### Team Profile
|
|
245
|
+
${tp ? `- Team Size: ${tp.teamSize || "Not set"}
|
|
246
|
+
- Has Designer: ${tp.hasDesigner ? "Yes" : "No"}
|
|
247
|
+
- Has Product Manager: ${tp.hasProductManager ? "Yes" : "No"}
|
|
248
|
+
- Has QA: ${tp.hasQA ? "Yes" : "No"}
|
|
249
|
+
- Development Approach: ${tp.developmentApproach || "Not set"}` : "Team profile not configured. Set up in Settings → Profile."}
|
|
250
|
+
|
|
251
|
+
### Branching Strategy
|
|
252
|
+
- Trunk-Based: ${bd?.trunkBased ? "Yes" : "No"}
|
|
253
|
+
- Auto-Merge: ${bd?.autoMerge ? "Yes" : "No"}
|
|
254
|
+
- Source: ${bd?.source || "default"}
|
|
255
|
+
${infra?.provider ? `
|
|
256
|
+
### Infrastructure
|
|
257
|
+
- Provider: ${infra.provider.toUpperCase()}
|
|
258
|
+
- Regions: ${infra.regions?.join(", ") || "Not set"}
|
|
259
|
+
- Compliance: ${infra.complianceFrameworks?.map((f) => f.toUpperCase()).join(", ") || "None"}` : ""}
|
|
260
|
+
`.trim();
|
|
261
|
+
};
|
|
262
|
+
const fetchBoardSettings = async () => {
|
|
263
|
+
try {
|
|
264
|
+
const boardId = await resolveBoardId();
|
|
265
|
+
if (!boardId)
|
|
266
|
+
return undefined;
|
|
267
|
+
const response = await client.get("/boards");
|
|
268
|
+
const boards = Array.isArray(response.data) ? response.data : [];
|
|
269
|
+
const board = boards.find((b) => b.id === boardId);
|
|
270
|
+
return board?.settings;
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
const formatDorDodSection = (boardSettings, dorChecklist, dodChecklist) => {
|
|
277
|
+
if (!boardSettings)
|
|
278
|
+
return "";
|
|
279
|
+
const sections = [];
|
|
280
|
+
const dorConfig = boardSettings.definitionOfReady;
|
|
281
|
+
if (dorConfig?.enabled && dorConfig.items?.length > 0) {
|
|
282
|
+
const cl = dorChecklist || {};
|
|
283
|
+
const lines = dorConfig.items.map((item) => {
|
|
284
|
+
const met = cl[item.id] === true;
|
|
285
|
+
return `- ${met ? "✅" : "❌"} ${item.label}`;
|
|
286
|
+
});
|
|
287
|
+
const unmetItems = dorConfig.items.filter((item) => cl[item.id] !== true);
|
|
288
|
+
const met = dorConfig.items.length - unmetItems.length;
|
|
289
|
+
sections.push(`### Definition of Ready (${met}/${dorConfig.items.length})`);
|
|
290
|
+
sections.push(lines.join("\n"));
|
|
291
|
+
if (unmetItems.length > 0) {
|
|
292
|
+
const unmetLabels = unmetItems.map((i) => i.label).join(", ");
|
|
293
|
+
sections.push(`\n> **DoR unmet:** ${unmetLabels}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const dodConfig = boardSettings.definitionOfDone;
|
|
297
|
+
if (dodConfig?.enabled && dodConfig.items?.length > 0) {
|
|
298
|
+
const cl = dodChecklist || {};
|
|
299
|
+
const lines = dodConfig.items.map((item) => {
|
|
300
|
+
const met = cl[item.id] === true;
|
|
301
|
+
return `- ${met ? "✅" : "❌"} ${item.label}`;
|
|
302
|
+
});
|
|
303
|
+
const unmetItems = dodConfig.items.filter((item) => cl[item.id] !== true);
|
|
304
|
+
const met = dodConfig.items.length - unmetItems.length;
|
|
305
|
+
sections.push(`### Definition of Done (${met}/${dodConfig.items.length})`);
|
|
306
|
+
sections.push(lines.join("\n"));
|
|
307
|
+
if (unmetItems.length > 0) {
|
|
308
|
+
const unmetLabels = unmetItems.map((i) => i.label).join(", ");
|
|
309
|
+
sections.push(`\n> **DoD unmet:** ${unmetLabels}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (sections.length === 0)
|
|
313
|
+
return "";
|
|
314
|
+
return `\n## Checklists\n${sections.join("\n")}\n`;
|
|
315
|
+
};
|
|
316
|
+
const formatDorWarnings = (boardSettings, dorChecklist) => {
|
|
317
|
+
if (!boardSettings)
|
|
318
|
+
return "";
|
|
319
|
+
const dorConfig = boardSettings.definitionOfReady;
|
|
320
|
+
if (!dorConfig?.enabled || dorConfig.enforcement !== "warn")
|
|
321
|
+
return "";
|
|
322
|
+
const cl = dorChecklist || {};
|
|
323
|
+
const unmetItems = (dorConfig.items || []).filter((item) => cl[item.id] !== true);
|
|
324
|
+
if (unmetItems.length === 0)
|
|
325
|
+
return "";
|
|
326
|
+
const warningLines = unmetItems.map((i) => `- ⚠️ DoR not met: ${i.label}`).join("\n");
|
|
327
|
+
return `\n## ⚠️ DoR Warnings\n${warningLines}\n\nConsider addressing these before starting implementation.\n`;
|
|
328
|
+
};
|
|
230
329
|
const fetchStories = async () => {
|
|
231
330
|
const boardId = await resolveBoardId();
|
|
232
331
|
const slug = normalizeBoardSlug(BOARD_SLUG);
|
|
@@ -652,6 +751,20 @@ const createServer = () => {
|
|
|
652
751
|
required: ["storyId"],
|
|
653
752
|
},
|
|
654
753
|
},
|
|
754
|
+
{
|
|
755
|
+
name: "review_pr",
|
|
756
|
+
description: "Run AI review on a story's PR diff against acceptance criteria and DoD. Fetches diff, runs AI validation, posts findings as GitHub PR comment, and stores results on the story.",
|
|
757
|
+
inputSchema: {
|
|
758
|
+
type: "object",
|
|
759
|
+
properties: {
|
|
760
|
+
storyId: {
|
|
761
|
+
type: "string",
|
|
762
|
+
description: "ID of the story to review",
|
|
763
|
+
},
|
|
764
|
+
},
|
|
765
|
+
required: ["storyId"],
|
|
766
|
+
},
|
|
767
|
+
},
|
|
655
768
|
] : [];
|
|
656
769
|
// Learning Loop tools (conditional on feature flag)
|
|
657
770
|
const learningLoopTools = learningLoopEnabled ? [
|
|
@@ -696,7 +809,7 @@ const createServer = () => {
|
|
|
696
809
|
try {
|
|
697
810
|
const toolName = request.params.name;
|
|
698
811
|
// Check TDD workflow tools
|
|
699
|
-
const tddWorkflowTools = ["start_story", "propose_test_plan", "submit_for_review"];
|
|
812
|
+
const tddWorkflowTools = ["start_story", "propose_test_plan", "submit_for_review", "review_pr"];
|
|
700
813
|
if (tddWorkflowTools.includes(toolName)) {
|
|
701
814
|
const enabled = await isTddWorkflowEnabled();
|
|
702
815
|
if (!enabled) {
|
|
@@ -886,11 +999,38 @@ ${config.infrastructureProfile?.provider ? `- Provider: ${config.infrastructureP
|
|
|
886
999
|
...(state ? { state } : {}),
|
|
887
1000
|
...(normalizedLane ? { lane: normalizedLane } : {}),
|
|
888
1001
|
}).filter(([, value]) => value !== undefined));
|
|
889
|
-
|
|
1002
|
+
let response;
|
|
1003
|
+
try {
|
|
1004
|
+
response = await client.patch(`/stories/${storyId}`, payload);
|
|
1005
|
+
}
|
|
1006
|
+
catch (err) {
|
|
1007
|
+
// DoR/DoD enforcement: surface 422 with unmet criteria to agent
|
|
1008
|
+
if (err.response?.status === 422 && err.response?.data?.unmetCriteria) {
|
|
1009
|
+
const errorMsg = err.response.data.error || "Checklist not met";
|
|
1010
|
+
const unmet = err.response.data.unmetCriteria;
|
|
1011
|
+
const unmetList = unmet.map((c) => `- ❌ ${c.label}`).join("\n");
|
|
1012
|
+
const isDod = errorMsg.includes("Done");
|
|
1013
|
+
const checklistField = isDod ? "dodChecklist" : "dorChecklist";
|
|
1014
|
+
return {
|
|
1015
|
+
content: [
|
|
1016
|
+
{
|
|
1017
|
+
type: "text",
|
|
1018
|
+
text: `# ⛔ ${errorMsg}\n\nCannot move story. The following criteria are unmet:\n\n${unmetList}\n\n**Action:** Update the story's \`${checklistField}\` to mark items as met, or ask the team to review the requirements in Board Settings.`,
|
|
1019
|
+
},
|
|
1020
|
+
],
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
throw err;
|
|
1024
|
+
}
|
|
1025
|
+
const data = response.data;
|
|
1026
|
+
let resultText = JSON.stringify(data, null, 2);
|
|
1027
|
+
// Surface DoR warnings to agent
|
|
1028
|
+
if (data.warnings && data.warnings.length > 0) {
|
|
1029
|
+
const warningList = data.warnings.map((w) => `- ⚠️ ${w}`).join("\n");
|
|
1030
|
+
resultText += `\n\n---\n# ⚠️ DoR Warnings\n\nStory moved to Current, but the following DoR items are unmet:\n\n${warningList}\n\nConsider addressing these before starting implementation.`;
|
|
1031
|
+
}
|
|
890
1032
|
return {
|
|
891
|
-
content: [
|
|
892
|
-
{ type: "text", text: JSON.stringify(response.data, null, 2) },
|
|
893
|
-
],
|
|
1033
|
+
content: [{ type: "text", text: resultText }],
|
|
894
1034
|
};
|
|
895
1035
|
}
|
|
896
1036
|
case "list_objectives": {
|
|
@@ -941,7 +1081,12 @@ ${config.infrastructureProfile?.provider ? `- Provider: ${config.infrastructureP
|
|
|
941
1081
|
const args = request.params.arguments;
|
|
942
1082
|
const { storyId } = args;
|
|
943
1083
|
assertUUID(storyId, "storyId");
|
|
944
|
-
|
|
1084
|
+
// Fetch story, team context, and board settings in parallel
|
|
1085
|
+
const [storyResponse, teamConfig, boardSettings] = await Promise.all([
|
|
1086
|
+
client.get(`/stories/${storyId}`),
|
|
1087
|
+
fetchFeatureConfig(),
|
|
1088
|
+
fetchBoardSettings(),
|
|
1089
|
+
]);
|
|
945
1090
|
const story = storyResponse.data;
|
|
946
1091
|
// Validation & Guardrails
|
|
947
1092
|
const storyLane = typeof story.lane === "string" ? story.lane.trim().toLowerCase() : "";
|
|
@@ -957,11 +1102,23 @@ ${config.infrastructureProfile?.provider ? `- Provider: ${config.infrastructureP
|
|
|
957
1102
|
story.learningGoals ||
|
|
958
1103
|
story.hypothesis ||
|
|
959
1104
|
"No specific learning goals identified.";
|
|
1105
|
+
const revisionFeedbackSection = story.test_plan_feedback
|
|
1106
|
+
? `\n## Test Plan Revision Feedback\n> ${story.test_plan_feedback.split("\n").join("\n> ")}\n\n> **Action:** Revise your test plan based on this feedback and call \`propose_test_plan\` again.\n`
|
|
1107
|
+
: "";
|
|
1108
|
+
let aiReviewSection = "";
|
|
1109
|
+
if (story.ai_review) {
|
|
1110
|
+
const r = story.ai_review;
|
|
1111
|
+
aiReviewSection = `\n## Previous AI Review\n> ${r.summary || "Review completed."}\n`;
|
|
1112
|
+
if (r.concerns?.length > 0) {
|
|
1113
|
+
aiReviewSection += "\n**Concerns to address:**\n" +
|
|
1114
|
+
r.concerns.map((c) => `- **${c.severity}:** ${c.description}`).join("\n") + "\n";
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
960
1117
|
const formattedBrief = `
|
|
961
1118
|
# Implementation Brief: ${story.title}
|
|
962
1119
|
|
|
963
1120
|
${statusWarning}
|
|
964
|
-
|
|
1121
|
+
${revisionFeedbackSection}${aiReviewSection}
|
|
965
1122
|
## Acceptance Criteria
|
|
966
1123
|
Here’s the acceptance criteria I’m going to satisfy:
|
|
967
1124
|
${acceptanceCriteria}
|
|
@@ -970,6 +1127,8 @@ ${acceptanceCriteria}
|
|
|
970
1127
|
Here are the assumptions I think we’re testing:
|
|
971
1128
|
${assumptions}
|
|
972
1129
|
|
|
1130
|
+
${formatTeamContext(teamConfig)}
|
|
1131
|
+
${formatDorDodSection(boardSettings, story.dorChecklist, story.dodChecklist)}
|
|
973
1132
|
## Proposal
|
|
974
1133
|
Here’s the smallest change that will validate it:
|
|
975
1134
|
[Agent should fill this in based on the context above]
|
|
@@ -986,12 +1145,19 @@ Here’s the smallest change that will validate it:
|
|
|
986
1145
|
throw new Error("storyId is required");
|
|
987
1146
|
}
|
|
988
1147
|
assertUUID(storyId, "storyId");
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1148
|
+
// Fetch story start, team config, and board settings in parallel
|
|
1149
|
+
const [response, teamConfig, boardSettings] = await Promise.all([
|
|
1150
|
+
client.post(`/stories/${storyId}/start`, {
|
|
1151
|
+
branchPrefix: branchPrefix || "feat",
|
|
1152
|
+
trunkBased,
|
|
1153
|
+
autoMerge,
|
|
1154
|
+
}),
|
|
1155
|
+
fetchFeatureConfig(),
|
|
1156
|
+
fetchBoardSettings(),
|
|
1157
|
+
]);
|
|
994
1158
|
const result = response.data;
|
|
1159
|
+
const teamContext = formatTeamContext(teamConfig);
|
|
1160
|
+
const dorWarnings = formatDorWarnings(boardSettings, result.dorChecklist);
|
|
995
1161
|
const isTrunk = result.trunkBased;
|
|
996
1162
|
const isAutoMerge = result.autoMerge;
|
|
997
1163
|
let formattedResult;
|
|
@@ -1016,7 +1182,9 @@ ${result.acceptance_criteria || "No specific AC provided."}
|
|
|
1016
1182
|
|
|
1017
1183
|
> **Feature Flag:** Wrap new behavior with \`${result.featureFlagName}\` for safe isolation.
|
|
1018
1184
|
> **TDD Workflow:** Write tests first, then call \`propose_test_plan\` before implementing.
|
|
1019
|
-
|
|
1185
|
+
|
|
1186
|
+
${teamContext}
|
|
1187
|
+
${dorWarnings}`;
|
|
1020
1188
|
}
|
|
1021
1189
|
else if (isTrunk && isAutoMerge) {
|
|
1022
1190
|
formattedResult = `
|
|
@@ -1040,7 +1208,9 @@ ${result.acceptance_criteria || "No specific AC provided."}
|
|
|
1040
1208
|
|
|
1041
1209
|
> **Feature Flag:** Wrap new behavior with \`${result.featureFlagName}\` for safe isolation.
|
|
1042
1210
|
> **TDD Workflow:** Write tests first, then call \`propose_test_plan\` before implementing.
|
|
1043
|
-
|
|
1211
|
+
|
|
1212
|
+
${teamContext}
|
|
1213
|
+
${dorWarnings}`;
|
|
1044
1214
|
}
|
|
1045
1215
|
else {
|
|
1046
1216
|
formattedResult = `
|
|
@@ -1056,7 +1226,9 @@ ${result.acceptance_criteria || "No specific AC provided."}
|
|
|
1056
1226
|
${result.workflow_reminder}
|
|
1057
1227
|
|
|
1058
1228
|
> **TDD Workflow:** Write tests first, then call \`propose_test_plan\` before implementing.
|
|
1059
|
-
|
|
1229
|
+
|
|
1230
|
+
${teamContext}
|
|
1231
|
+
${dorWarnings}`;
|
|
1060
1232
|
}
|
|
1061
1233
|
return {
|
|
1062
1234
|
content: [{ type: "text", text: formattedResult.trim() }],
|
|
@@ -1072,17 +1244,29 @@ ${result.workflow_reminder}
|
|
|
1072
1244
|
if (!testPlan) {
|
|
1073
1245
|
throw new Error("testPlan is required");
|
|
1074
1246
|
}
|
|
1247
|
+
// Fetch story to check for previous revision feedback
|
|
1248
|
+
let previousFeedback = null;
|
|
1249
|
+
try {
|
|
1250
|
+
const storyResponse = await client.get(`/stories/${storyId}`);
|
|
1251
|
+
previousFeedback = storyResponse.data?.test_plan_feedback || null;
|
|
1252
|
+
}
|
|
1253
|
+
catch (_) {
|
|
1254
|
+
// Non-blocking — proceed without feedback context
|
|
1255
|
+
}
|
|
1075
1256
|
const response = await client.post(`/stories/${storyId}/propose-tests`, {
|
|
1076
1257
|
testPlan,
|
|
1077
1258
|
testFilePaths,
|
|
1078
1259
|
});
|
|
1079
1260
|
const result = response.data;
|
|
1261
|
+
const feedbackSection = previousFeedback
|
|
1262
|
+
? `\n## Previous Revision Feedback\n> ${previousFeedback.split("\n").join("\n> ")}\n`
|
|
1263
|
+
: "";
|
|
1080
1264
|
const formattedResult = `
|
|
1081
1265
|
# Test Plan Proposed
|
|
1082
1266
|
|
|
1083
1267
|
**Story ID:** ${storyId}
|
|
1084
1268
|
**Workflow Stage:** ${result.workflow_stage}
|
|
1085
|
-
|
|
1269
|
+
${feedbackSection}
|
|
1086
1270
|
## Test Files
|
|
1087
1271
|
${(result.test_file_paths || []).map((p) => `- \`${p}\``).join("\n") || "No test files specified"}
|
|
1088
1272
|
|
|
@@ -1151,6 +1335,14 @@ git branch -d ${instr.sourceBranch}
|
|
|
1151
1335
|
\`\`\`
|
|
1152
1336
|
`;
|
|
1153
1337
|
}
|
|
1338
|
+
// Format PR section if PR was created or failed
|
|
1339
|
+
let prSection = "";
|
|
1340
|
+
if (result.pr_url && result.pr_number) {
|
|
1341
|
+
prSection = `\n## Pull Request\n**PR:** [#${result.pr_number}](${result.pr_url})\n**Status:** ${result.pr_state || "open"}\n> DoD checklist and acceptance criteria included in PR body.\n`;
|
|
1342
|
+
}
|
|
1343
|
+
else if (result.pr_error) {
|
|
1344
|
+
prSection = `\n## Pull Request\n> PR creation failed: ${result.pr_error}\n> Workflow proceeded successfully — create PR manually if needed.\n`;
|
|
1345
|
+
}
|
|
1154
1346
|
const formattedResult = `
|
|
1155
1347
|
# Implementation Submitted for Review
|
|
1156
1348
|
|
|
@@ -1166,7 +1358,7 @@ ${result.featureFlagName ? `**Feature Flag:** \`${result.featureFlagName}\`` : "
|
|
|
1166
1358
|
|
|
1167
1359
|
## Commits
|
|
1168
1360
|
${(result.commit_hashes || []).map((h) => `- \`${h}\``).join("\n") || "No commits recorded"}
|
|
1169
|
-
${autoMergeSection}
|
|
1361
|
+
${autoMergeSection}${prSection}
|
|
1170
1362
|
## Next Step
|
|
1171
1363
|
${result.next_step}
|
|
1172
1364
|
|
|
@@ -1178,6 +1370,46 @@ ${result.next_step}
|
|
|
1178
1370
|
content: [{ type: "text", text: formattedResult.trim() }],
|
|
1179
1371
|
};
|
|
1180
1372
|
}
|
|
1373
|
+
case "review_pr": {
|
|
1374
|
+
const args = request.params.arguments;
|
|
1375
|
+
const { storyId } = args;
|
|
1376
|
+
if (!storyId)
|
|
1377
|
+
throw new Error("storyId is required");
|
|
1378
|
+
assertUUID(storyId, "storyId");
|
|
1379
|
+
const reviewResponse = await client.post(`/stories/${storyId}/ai-review`);
|
|
1380
|
+
const reviewResult = reviewResponse.data;
|
|
1381
|
+
const review = reviewResult.ai_review;
|
|
1382
|
+
let acSection = "";
|
|
1383
|
+
if (review?.acCoverage?.length > 0) {
|
|
1384
|
+
acSection = "\n## Acceptance Criteria Coverage\n" +
|
|
1385
|
+
review.acCoverage.map((ac) => {
|
|
1386
|
+
const icon = ac.status === "met" ? "+" : ac.status === "partial" ? "~" : "-";
|
|
1387
|
+
return `[${icon}] **${ac.status.toUpperCase()}:** ${ac.criterion}\n ${ac.evidence}`;
|
|
1388
|
+
}).join("\n");
|
|
1389
|
+
}
|
|
1390
|
+
let dodSection = "";
|
|
1391
|
+
if (review?.dodValidation?.length > 0) {
|
|
1392
|
+
dodSection = "\n\n## Definition of Done Validation\n" +
|
|
1393
|
+
review.dodValidation.map((d) => `[${d.aiVerified ? "x" : " "}] ${d.label} — ${d.note}`).join("\n");
|
|
1394
|
+
}
|
|
1395
|
+
let concernsSection = "";
|
|
1396
|
+
if (review?.concerns?.length > 0) {
|
|
1397
|
+
concernsSection = "\n\n## Concerns\n" +
|
|
1398
|
+
review.concerns.map((c) => `- **${c.severity}:** ${c.description}${c.file ? ` (${c.file}${c.line ? `:${c.line}` : ""})` : ""}`).join("\n");
|
|
1399
|
+
}
|
|
1400
|
+
const formattedReview = `
|
|
1401
|
+
# AI Review — Story ${storyId.substring(0, 8)}
|
|
1402
|
+
|
|
1403
|
+
${review?.summary || "Review completed."}
|
|
1404
|
+
${acSection}${dodSection}${concernsSection}
|
|
1405
|
+
|
|
1406
|
+
---
|
|
1407
|
+
_Generated by Elixium AI Review Intelligence_
|
|
1408
|
+
`.trim();
|
|
1409
|
+
return {
|
|
1410
|
+
content: [{ type: "text", text: formattedReview }],
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1181
1413
|
case "estimate_cost": {
|
|
1182
1414
|
const args = request.params.arguments;
|
|
1183
1415
|
const { storyId, constraints } = args;
|
package/package.json
CHANGED