@kaddo/mcp 3.25.1 → 3.27.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 +285 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2990,6 +2990,235 @@ function serializeDriftJson(r) {
|
|
|
2990
2990
|
return JSON.stringify(out, null, 2) + "\n";
|
|
2991
2991
|
}
|
|
2992
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
|
+
|
|
2993
3222
|
// src/workitems.ts
|
|
2994
3223
|
import matter7 from "gray-matter";
|
|
2995
3224
|
var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["draft", "ready", "in-progress", "blocked"]);
|
|
@@ -3240,6 +3469,31 @@ var RESOURCES = [
|
|
|
3240
3469
|
}
|
|
3241
3470
|
return [{ uri: "kaddo://guard-history", text: runs, mimeType: "application/jsonl" }];
|
|
3242
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
|
+
}
|
|
3243
3497
|
}
|
|
3244
3498
|
];
|
|
3245
3499
|
|
|
@@ -4049,6 +4303,21 @@ function generateDriftReport(root, opts = {}) {
|
|
|
4049
4303
|
next_suggested_resources: ["kaddo://drift-report"]
|
|
4050
4304
|
};
|
|
4051
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
|
+
}
|
|
4052
4321
|
function generateCapsuleDraft(root) {
|
|
4053
4322
|
const config = requireConfig(root);
|
|
4054
4323
|
requireKnowledge(root);
|
|
@@ -4282,6 +4551,22 @@ function createServer(root) {
|
|
|
4282
4551
|
() => generateDriftReport(root, { format: args.format, output: args.output })
|
|
4283
4552
|
)
|
|
4284
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
|
+
);
|
|
4285
4570
|
server.registerTool(
|
|
4286
4571
|
"kaddo_generate_capsule_draft",
|
|
4287
4572
|
{ title: "Generate capsule draft", description: `Write a capsule DRAFT under .kaddo/exports/ (never registers it). ${DERIVED_NOTE}`, inputSchema: {} },
|
package/package.json
CHANGED