@kaddo/mcp 3.25.0 → 3.26.0
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 +330 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2702,11 +2702,23 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2702
2702
|
const graphMult = GRAPH_MULT[graphQuality] ?? 0;
|
|
2703
2703
|
const archH = round2(a.architecture_discovery_hours_saved_when_graph_good * graphMult);
|
|
2704
2704
|
const guard = buildGuardHistory(dir);
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
hours:
|
|
2708
|
-
|
|
2709
|
-
|
|
2705
|
+
let driftSavings;
|
|
2706
|
+
if (!guard.available) {
|
|
2707
|
+
driftSavings = { available: false, hours: 0, reason: "Guard history is not persisted yet." };
|
|
2708
|
+
} else if (guard.resolved > 0) {
|
|
2709
|
+
driftSavings = {
|
|
2710
|
+
available: true,
|
|
2711
|
+
hours: round2(guard.resolved * a.rework_hours_avoided_per_resolved_drift),
|
|
2712
|
+
formula: `${guard.resolved} resolved drift warnings \xD7 ${a.rework_hours_avoided_per_resolved_drift} h`
|
|
2713
|
+
};
|
|
2714
|
+
} else {
|
|
2715
|
+
driftSavings = {
|
|
2716
|
+
available: true,
|
|
2717
|
+
hours: 0,
|
|
2718
|
+
formula: `0 resolved drift warnings \xD7 ${a.rework_hours_avoided_per_resolved_drift} h`,
|
|
2719
|
+
reason: "Guard history exists, but no resolved drift warnings have been recorded yet."
|
|
2720
|
+
};
|
|
2721
|
+
}
|
|
2710
2722
|
const totalHours = round2(ctxPrepH + reviewH + clarH + onboardH + archH + driftSavings.hours);
|
|
2711
2723
|
const totalValue = Math.round(totalHours * a.hourly_cost);
|
|
2712
2724
|
const reasons = [];
|
|
@@ -2721,15 +2733,27 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2721
2733
|
level = "Medium";
|
|
2722
2734
|
reasons.push("Strong knowledge and graph evidence.");
|
|
2723
2735
|
}
|
|
2724
|
-
if (!guard.available)
|
|
2725
|
-
|
|
2736
|
+
if (!guard.available) {
|
|
2737
|
+
reasons.push("Guard history is not persisted yet \u2014 avoided rework is not estimated.");
|
|
2738
|
+
} else if (guard.resolved > 0) {
|
|
2739
|
+
reasons.push(`Guard history available (${guard.resolved} resolved drift warning(s)).`);
|
|
2740
|
+
} else {
|
|
2741
|
+
reasons.push("Guard history available, but no resolved drift warnings have been recorded yet.");
|
|
2742
|
+
}
|
|
2726
2743
|
reasons.push(source === "file" ? "Assumptions come from `.kaddo/savings.yml`." : "Savings assumptions are defaults and should be calibrated.");
|
|
2727
2744
|
const actions = [];
|
|
2728
2745
|
if (source === "default") actions.push("Run `kaddo savings init` to customize assumptions.");
|
|
2729
2746
|
actions.push("Calibrate hourly cost and team assumptions with real team data.");
|
|
2730
2747
|
actions.push("Run `kaddo impact` to verify the underlying evidence quality.");
|
|
2731
2748
|
actions.push("Use this report as directional evidence, not accounting data.");
|
|
2732
|
-
|
|
2749
|
+
if (!guard.available) {
|
|
2750
|
+
actions.push("Future: persist Guard history to estimate avoided rework.");
|
|
2751
|
+
} else if (guard.resolved === 0) {
|
|
2752
|
+
actions.push("Continue running `kaddo guard --record` before important commits to capture resolved drift warnings.");
|
|
2753
|
+
if (guard.open > 0) actions.push("Review open drift warnings with `kaddo drift`.");
|
|
2754
|
+
} else {
|
|
2755
|
+
actions.push("Use drift history to calibrate `rework_hours_avoided_per_resolved_drift`.");
|
|
2756
|
+
}
|
|
2733
2757
|
return {
|
|
2734
2758
|
generated_at: now.toISOString(),
|
|
2735
2759
|
project: impact.project,
|
|
@@ -2755,7 +2779,9 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2755
2779
|
graph_nodes: impact.graph_quality.available ? impact.graph_quality.nodes : null,
|
|
2756
2780
|
graph_edges: impact.graph_quality.available ? impact.graph_quality.edges : null,
|
|
2757
2781
|
context_readiness: readiness,
|
|
2758
|
-
guard_history_available: guard.available
|
|
2782
|
+
guard_history_available: guard.available,
|
|
2783
|
+
guard_runs_recorded: guard.total_runs,
|
|
2784
|
+
resolved_drift_warnings: guard.resolved
|
|
2759
2785
|
},
|
|
2760
2786
|
estimated_savings: {
|
|
2761
2787
|
context_preparation: { hours: ctxPrepH, formula: `${completed} Work Items \xD7 ${a.context_preparation_minutes_saved_per_work_item} min` },
|
|
@@ -2805,7 +2831,13 @@ function renderSavingsMarkdown(r) {
|
|
|
2805
2831
|
if (e.graph_nodes !== null) L.push(`- Graph nodes: ${e.graph_nodes}`);
|
|
2806
2832
|
if (e.graph_edges !== null) L.push(`- Graph edges: ${e.graph_edges}`);
|
|
2807
2833
|
L.push(`- Context readiness: ${e.context_readiness}`);
|
|
2808
|
-
|
|
2834
|
+
if (e.guard_history_available) {
|
|
2835
|
+
L.push("- Guard history: available");
|
|
2836
|
+
L.push(`- Guard runs recorded: ${e.guard_runs_recorded}`);
|
|
2837
|
+
L.push(`- Resolved drift warnings: ${e.resolved_drift_warnings}`);
|
|
2838
|
+
} else {
|
|
2839
|
+
L.push("- Guard history: not available");
|
|
2840
|
+
}
|
|
2809
2841
|
L.push("");
|
|
2810
2842
|
L.push("## Estimated Savings", "");
|
|
2811
2843
|
const s = r.estimated_savings;
|
|
@@ -2822,7 +2854,9 @@ function renderSavingsMarkdown(r) {
|
|
|
2822
2854
|
L.push("### Drift Prevention", "");
|
|
2823
2855
|
if (s.drift_prevention.available) {
|
|
2824
2856
|
L.push(`- Formula: ${s.drift_prevention.formula}`);
|
|
2825
|
-
L.push(`- Estimated: ${s.drift_prevention.hours} h
|
|
2857
|
+
L.push(`- Estimated: ${s.drift_prevention.hours} h`);
|
|
2858
|
+
if (s.drift_prevention.reason) L.push(`- Reason: ${s.drift_prevention.reason}`);
|
|
2859
|
+
L.push("");
|
|
2826
2860
|
} else {
|
|
2827
2861
|
L.push("- Not available yet.");
|
|
2828
2862
|
L.push(`- Reason: ${s.drift_prevention.reason}`, "");
|
|
@@ -2956,6 +2990,235 @@ function serializeDriftJson(r) {
|
|
|
2956
2990
|
return JSON.stringify(out, null, 2) + "\n";
|
|
2957
2991
|
}
|
|
2958
2992
|
|
|
2993
|
+
// ../cli/src/core/open-questions.ts
|
|
2994
|
+
var SOURCES = [
|
|
2995
|
+
"knowledge/business/business.md",
|
|
2996
|
+
"knowledge/product/product.md",
|
|
2997
|
+
"knowledge/tech/codebase.md",
|
|
2998
|
+
"knowledge/delivery/roadmap.md"
|
|
2999
|
+
];
|
|
3000
|
+
var BLOCKING = [
|
|
3001
|
+
"mvp",
|
|
3002
|
+
"stack",
|
|
3003
|
+
"arquitect",
|
|
3004
|
+
"architecture",
|
|
3005
|
+
"api",
|
|
3006
|
+
"web app",
|
|
3007
|
+
"webapp",
|
|
3008
|
+
"cli",
|
|
3009
|
+
"backend",
|
|
3010
|
+
"auth",
|
|
3011
|
+
"autenticaci",
|
|
3012
|
+
"persist",
|
|
3013
|
+
"base de datos",
|
|
3014
|
+
"database",
|
|
3015
|
+
"roadmap",
|
|
3016
|
+
"alcance",
|
|
3017
|
+
"scope",
|
|
3018
|
+
"primera versi\xF3n",
|
|
3019
|
+
"primera version",
|
|
3020
|
+
"first version",
|
|
3021
|
+
"fastify",
|
|
3022
|
+
"nestjs",
|
|
3023
|
+
"framework"
|
|
3024
|
+
];
|
|
3025
|
+
var DEFERRED = [
|
|
3026
|
+
"integraci",
|
|
3027
|
+
"integration",
|
|
3028
|
+
"analytics",
|
|
3029
|
+
"notificaci",
|
|
3030
|
+
"notification",
|
|
3031
|
+
"google calendar",
|
|
3032
|
+
"eventbrite",
|
|
3033
|
+
"meetup",
|
|
3034
|
+
"dashboard",
|
|
3035
|
+
"multi-tenant",
|
|
3036
|
+
"multitenant",
|
|
3037
|
+
"mobile",
|
|
3038
|
+
"m\xF3vil",
|
|
3039
|
+
"pago",
|
|
3040
|
+
"payment",
|
|
3041
|
+
"ticketing",
|
|
3042
|
+
"multiple tracks",
|
|
3043
|
+
"m\xFAltiples tracks",
|
|
3044
|
+
"webhook"
|
|
3045
|
+
];
|
|
3046
|
+
var IMPORTANT = [
|
|
3047
|
+
"validaci",
|
|
3048
|
+
"validation",
|
|
3049
|
+
"paginaci",
|
|
3050
|
+
"pagination",
|
|
3051
|
+
"soft delete",
|
|
3052
|
+
"sponsor",
|
|
3053
|
+
"agenda",
|
|
3054
|
+
"ubicaci",
|
|
3055
|
+
"location",
|
|
3056
|
+
"rol",
|
|
3057
|
+
"role",
|
|
3058
|
+
"permis",
|
|
3059
|
+
"permission"
|
|
3060
|
+
];
|
|
3061
|
+
function matchesAny(text2, keywords) {
|
|
3062
|
+
return keywords.some((k) => text2.includes(k));
|
|
3063
|
+
}
|
|
3064
|
+
function classifyQuestion(question) {
|
|
3065
|
+
const t = question.toLowerCase();
|
|
3066
|
+
if (matchesAny(t, BLOCKING)) {
|
|
3067
|
+
return { classification: "blocking", reason: "Affects scope, architecture or the first Work Items." };
|
|
3068
|
+
}
|
|
3069
|
+
if (matchesAny(t, DEFERRED) && !matchesAny(t, IMPORTANT)) {
|
|
3070
|
+
return { classification: "deferred", reason: "Can be moved to a later phase." };
|
|
3071
|
+
}
|
|
3072
|
+
if (matchesAny(t, IMPORTANT)) {
|
|
3073
|
+
return { classification: "important", reason: "Relevant, but a temporary assumption can unblock progress." };
|
|
3074
|
+
}
|
|
3075
|
+
return { classification: "important", reason: "Relevant, but a temporary assumption can unblock progress." };
|
|
3076
|
+
}
|
|
3077
|
+
function extractFromMarkdown(md) {
|
|
3078
|
+
const lines = md.split(/\r?\n/);
|
|
3079
|
+
const start = lines.findIndex((l) => /^#{1,6}\s+open questions\s*$/i.test(l.trim()) || /^#{1,6}\s+preguntas abiertas\s*$/i.test(l.trim()));
|
|
3080
|
+
if (start < 0) return [];
|
|
3081
|
+
const out = [];
|
|
3082
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
3083
|
+
const line = lines[i];
|
|
3084
|
+
if (/^#{1,6}\s+/.test(line)) break;
|
|
3085
|
+
const m = line.match(/^\s*(?:[-*]|\d+\.)\s+(.+?)\s*$/);
|
|
3086
|
+
if (m) {
|
|
3087
|
+
const q = m[1].trim();
|
|
3088
|
+
if (q && !/^_.*_$/.test(q)) out.push(q);
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
return out;
|
|
3092
|
+
}
|
|
3093
|
+
function suggestedAssumption(question) {
|
|
3094
|
+
const t = question.toLowerCase();
|
|
3095
|
+
if (matchesAny(t, ["api", "web app", "webapp", "cli", "backend"])) {
|
|
3096
|
+
return "Start as a backend API to keep the initial scope small.";
|
|
3097
|
+
}
|
|
3098
|
+
if (matchesAny(t, ["auth", "autenticaci"])) {
|
|
3099
|
+
return "No authentication in the MVP.";
|
|
3100
|
+
}
|
|
3101
|
+
if (matchesAny(t, ["fastify", "nestjs", "framework", "stack"])) {
|
|
3102
|
+
return "Choose the lighter stack option to minimize MVP scope.";
|
|
3103
|
+
}
|
|
3104
|
+
if (matchesAny(t, ["persist", "base de datos", "database"])) {
|
|
3105
|
+
return "Use a simple local datastore (e.g. SQLite) for the MVP.";
|
|
3106
|
+
}
|
|
3107
|
+
return "Proceed with the smallest reasonable MVP interpretation and revisit later.";
|
|
3108
|
+
}
|
|
3109
|
+
function buildOpenQuestionsReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
3110
|
+
const project = loadConfig(dir)?.project.name ?? "unknown";
|
|
3111
|
+
const questions = [];
|
|
3112
|
+
let seq = 0;
|
|
3113
|
+
for (const source of SOURCES) {
|
|
3114
|
+
const p = join(dir, source);
|
|
3115
|
+
if (!exists(p)) continue;
|
|
3116
|
+
let md;
|
|
3117
|
+
try {
|
|
3118
|
+
md = readFile(p);
|
|
3119
|
+
} catch {
|
|
3120
|
+
continue;
|
|
3121
|
+
}
|
|
3122
|
+
for (const q of extractFromMarkdown(md)) {
|
|
3123
|
+
seq += 1;
|
|
3124
|
+
const { classification, reason } = classifyQuestion(q);
|
|
3125
|
+
questions.push({
|
|
3126
|
+
id: `OQ-${String(seq).padStart(3, "0")}`,
|
|
3127
|
+
source,
|
|
3128
|
+
section: "Open Questions",
|
|
3129
|
+
question: q,
|
|
3130
|
+
classification,
|
|
3131
|
+
reason,
|
|
3132
|
+
...classification === "blocking" ? { suggested_assumption: suggestedAssumption(q) } : {}
|
|
3133
|
+
});
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
const blocking = questions.filter((q) => q.classification === "blocking");
|
|
3137
|
+
const important = questions.filter((q) => q.classification === "important");
|
|
3138
|
+
const deferred = questions.filter((q) => q.classification === "deferred");
|
|
3139
|
+
let readiness;
|
|
3140
|
+
if (questions.length === 0) readiness = "unknown";
|
|
3141
|
+
else if (blocking.length > 0) readiness = "needs_decisions";
|
|
3142
|
+
else readiness = "ready";
|
|
3143
|
+
const suggested_assumptions = blocking.map((q) => q.suggested_assumption).filter(Boolean);
|
|
3144
|
+
const recommended_next_step = readiness === "needs_decisions" ? "Confirm suggested assumptions with the user (resolve, assume or defer blocking questions) before generating the roadmap." : readiness === "ready" ? "No blocking questions \u2014 you can proceed to the roadmap (review important questions when convenient)." : "No open questions found. Run `kaddo bootstrap` to capture early questions, or proceed.";
|
|
3145
|
+
return {
|
|
3146
|
+
generated_at: now.toISOString(),
|
|
3147
|
+
project,
|
|
3148
|
+
summary: {
|
|
3149
|
+
open_questions: questions.length,
|
|
3150
|
+
roadmap_readiness: readiness,
|
|
3151
|
+
blocking: blocking.length,
|
|
3152
|
+
important: important.length,
|
|
3153
|
+
deferred: deferred.length
|
|
3154
|
+
},
|
|
3155
|
+
questions,
|
|
3156
|
+
blocking_questions: blocking,
|
|
3157
|
+
important_questions: important,
|
|
3158
|
+
deferred_questions: deferred,
|
|
3159
|
+
suggested_assumptions,
|
|
3160
|
+
recommended_next_step
|
|
3161
|
+
};
|
|
3162
|
+
}
|
|
3163
|
+
function renderOpenQuestionsMarkdown(r) {
|
|
3164
|
+
const L = [];
|
|
3165
|
+
L.push("# Kaddo Open Questions Readiness Report", "");
|
|
3166
|
+
L.push(`Generated at: ${r.generated_at}`);
|
|
3167
|
+
L.push(`Project: ${r.project}`);
|
|
3168
|
+
L.push("");
|
|
3169
|
+
L.push("## Summary", "");
|
|
3170
|
+
L.push(`- Open questions found: ${r.summary.open_questions}`);
|
|
3171
|
+
L.push(`- Roadmap readiness: ${r.summary.roadmap_readiness}`);
|
|
3172
|
+
L.push(`- Blocking: ${r.summary.blocking}`);
|
|
3173
|
+
L.push(`- Important: ${r.summary.important}`);
|
|
3174
|
+
L.push(`- Deferred: ${r.summary.deferred}`);
|
|
3175
|
+
L.push("");
|
|
3176
|
+
if (r.summary.open_questions === 0) {
|
|
3177
|
+
L.push("No `## Open Questions` sections found in business / product / codebase / roadmap.", "");
|
|
3178
|
+
L.push("## Recommended Next Step", "", r.recommended_next_step, "");
|
|
3179
|
+
return L.join("\n");
|
|
3180
|
+
}
|
|
3181
|
+
L.push("## Blocking Questions", "");
|
|
3182
|
+
if (r.blocking_questions.length === 0) L.push("_None._", "");
|
|
3183
|
+
for (const q of r.blocking_questions) {
|
|
3184
|
+
L.push(`### ${q.id}`, "");
|
|
3185
|
+
L.push(`- Source: \`${q.source}\``);
|
|
3186
|
+
L.push(`- Question: ${q.question}`);
|
|
3187
|
+
L.push(`- Reason: ${q.reason}`);
|
|
3188
|
+
if (q.suggested_assumption) L.push(`- Suggested assumption: ${q.suggested_assumption}`);
|
|
3189
|
+
L.push("");
|
|
3190
|
+
}
|
|
3191
|
+
L.push("## Important Questions", "");
|
|
3192
|
+
if (r.important_questions.length === 0) L.push("_None._");
|
|
3193
|
+
for (const q of r.important_questions) L.push(`- ${q.question}`);
|
|
3194
|
+
L.push("");
|
|
3195
|
+
L.push("## Deferred Questions", "");
|
|
3196
|
+
if (r.deferred_questions.length === 0) L.push("_None._");
|
|
3197
|
+
for (const q of r.deferred_questions) L.push(`- ${q.question}`);
|
|
3198
|
+
L.push("");
|
|
3199
|
+
if (r.suggested_assumptions.length > 0) {
|
|
3200
|
+
L.push("## Suggested Assumptions", "");
|
|
3201
|
+
for (const a of r.suggested_assumptions) L.push(`- ${a}`);
|
|
3202
|
+
L.push("");
|
|
3203
|
+
}
|
|
3204
|
+
L.push("## Recommended Next Step", "", r.recommended_next_step, "");
|
|
3205
|
+
return L.join("\n");
|
|
3206
|
+
}
|
|
3207
|
+
function serializeOpenQuestionsJson(r) {
|
|
3208
|
+
return JSON.stringify(r, null, 2) + "\n";
|
|
3209
|
+
}
|
|
3210
|
+
function roadmapReadinessSummary(dir) {
|
|
3211
|
+
const r = buildOpenQuestionsReport(dir);
|
|
3212
|
+
return {
|
|
3213
|
+
roadmap_readiness: r.summary.roadmap_readiness,
|
|
3214
|
+
blocking_questions: r.summary.blocking,
|
|
3215
|
+
important_questions: r.summary.important,
|
|
3216
|
+
deferred_questions: r.summary.deferred,
|
|
3217
|
+
suggested_assumptions: r.suggested_assumptions,
|
|
3218
|
+
recommended_next_step: r.summary.roadmap_readiness === "needs_decisions" ? "Resolve, assume, or defer blocking questions before generating roadmap." : r.recommended_next_step
|
|
3219
|
+
};
|
|
3220
|
+
}
|
|
3221
|
+
|
|
2959
3222
|
// src/workitems.ts
|
|
2960
3223
|
import matter7 from "gray-matter";
|
|
2961
3224
|
var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["draft", "ready", "in-progress", "blocked"]);
|
|
@@ -3206,6 +3469,31 @@ var RESOURCES = [
|
|
|
3206
3469
|
}
|
|
3207
3470
|
return [{ uri: "kaddo://guard-history", text: runs, mimeType: "application/jsonl" }];
|
|
3208
3471
|
}
|
|
3472
|
+
},
|
|
3473
|
+
{
|
|
3474
|
+
uri: "kaddo://open-questions",
|
|
3475
|
+
name: "Kaddo open questions",
|
|
3476
|
+
description: "Open questions from business/product/codebase/roadmap, classified for the readiness gate.",
|
|
3477
|
+
mimeType: "application/json",
|
|
3478
|
+
read: (root) => {
|
|
3479
|
+
if (!hasKnowledge(root)) {
|
|
3480
|
+
return text("kaddo://open-questions", "Knowledge repository not found. Run `kaddo bootstrap` first.", "text/plain");
|
|
3481
|
+
}
|
|
3482
|
+
const r = buildOpenQuestionsReport(root);
|
|
3483
|
+
return [{ uri: "kaddo://open-questions", text: JSON.stringify({ questions: r.questions }, null, 2), mimeType: "application/json" }];
|
|
3484
|
+
}
|
|
3485
|
+
},
|
|
3486
|
+
{
|
|
3487
|
+
uri: "kaddo://roadmap-readiness",
|
|
3488
|
+
name: "Kaddo roadmap readiness",
|
|
3489
|
+
description: "Decision-oriented roadmap readiness summary (blocking/important/deferred + suggested assumptions).",
|
|
3490
|
+
mimeType: "application/json",
|
|
3491
|
+
read: (root) => {
|
|
3492
|
+
if (!hasKnowledge(root)) {
|
|
3493
|
+
return text("kaddo://roadmap-readiness", "Knowledge repository not found. Run `kaddo bootstrap` first.", "text/plain");
|
|
3494
|
+
}
|
|
3495
|
+
return [{ uri: "kaddo://roadmap-readiness", text: JSON.stringify(roadmapReadinessSummary(root), null, 2), mimeType: "application/json" }];
|
|
3496
|
+
}
|
|
3209
3497
|
}
|
|
3210
3498
|
];
|
|
3211
3499
|
|
|
@@ -4015,6 +4303,21 @@ function generateDriftReport(root, opts = {}) {
|
|
|
4015
4303
|
next_suggested_resources: ["kaddo://drift-report"]
|
|
4016
4304
|
};
|
|
4017
4305
|
}
|
|
4306
|
+
function generateQuestionsReport(root, opts = {}) {
|
|
4307
|
+
requireConfig(root);
|
|
4308
|
+
const format = opts.format ?? "markdown";
|
|
4309
|
+
const report = buildOpenQuestionsReport(root);
|
|
4310
|
+
const content = format === "json" ? serializeOpenQuestionsJson(report) : renderOpenQuestionsMarkdown(report);
|
|
4311
|
+
const out = opts.output ?? `.kaddo/reports/questions-report.${format === "json" ? "json" : "md"}`;
|
|
4312
|
+
writeDerived(root, out, content);
|
|
4313
|
+
return {
|
|
4314
|
+
status: "ok",
|
|
4315
|
+
files_written: [out.replace(/\\/g, "/")],
|
|
4316
|
+
summary: `Open questions report generated (readiness: ${report.summary.roadmap_readiness}, ${report.summary.blocking} blocking).`,
|
|
4317
|
+
warnings: report.summary.blocking > 0 ? [`${report.summary.blocking} blocking question(s) before the roadmap.`] : [],
|
|
4318
|
+
next_suggested_resources: ["kaddo://open-questions", "kaddo://roadmap-readiness"]
|
|
4319
|
+
};
|
|
4320
|
+
}
|
|
4018
4321
|
function generateCapsuleDraft(root) {
|
|
4019
4322
|
const config = requireConfig(root);
|
|
4020
4323
|
requireKnowledge(root);
|
|
@@ -4248,6 +4551,22 @@ function createServer(root) {
|
|
|
4248
4551
|
() => generateDriftReport(root, { format: args.format, output: args.output })
|
|
4249
4552
|
)
|
|
4250
4553
|
);
|
|
4554
|
+
server.registerTool(
|
|
4555
|
+
"kaddo_generate_questions_report",
|
|
4556
|
+
{
|
|
4557
|
+
title: "Generate open-questions report",
|
|
4558
|
+
description: `Write the Open Questions readiness report under .kaddo/reports/ (classifies blocking/important/deferred). Does NOT resolve questions. ${DERIVED_NOTE}`,
|
|
4559
|
+
inputSchema: {
|
|
4560
|
+
format: z2.enum(["markdown", "json"]).optional(),
|
|
4561
|
+
output: z2.string().optional()
|
|
4562
|
+
}
|
|
4563
|
+
},
|
|
4564
|
+
async (args) => generated(
|
|
4565
|
+
root,
|
|
4566
|
+
"generate open-questions report",
|
|
4567
|
+
() => generateQuestionsReport(root, { format: args.format, output: args.output })
|
|
4568
|
+
)
|
|
4569
|
+
);
|
|
4251
4570
|
server.registerTool(
|
|
4252
4571
|
"kaddo_generate_capsule_draft",
|
|
4253
4572
|
{ title: "Generate capsule draft", description: `Write a capsule DRAFT under .kaddo/exports/ (never registers it). ${DERIVED_NOTE}`, inputSchema: {} },
|
package/package.json
CHANGED