@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.
Files changed (2) hide show
  1. package/dist/index.js +285 -0
  2. 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.25.1",
3
+ "version": "3.27.0",
4
4
  "description": "MCP server for Kaddo project knowledge: read-only resources/tools/prompts plus safe derived generation under .kaddo/.",
5
5
  "license": "MIT",
6
6
  "repository": {