@kaddo/mcp 3.117.0 → 3.119.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 +544 -98
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -389,7 +389,7 @@ function join(...parts) {
389
389
  }
390
390
 
391
391
  // ../cli/src/core/impact-report.ts
392
- import matter10 from "gray-matter";
392
+ import matter11 from "gray-matter";
393
393
 
394
394
  // ../cli/src/core/config.ts
395
395
  import { z } from "zod";
@@ -691,12 +691,12 @@ function classifyLayer(posixPath) {
691
691
  return "unknown";
692
692
  }
693
693
  function enrich(dir, a2) {
694
- const posix = toPosix2(a2.filePath);
694
+ const posix3 = toPosix2(a2.filePath);
695
695
  const isWi = isWorkItemArtifact(a2);
696
696
  return {
697
697
  ...a2,
698
698
  relPath: relativeTo(dir, a2.filePath),
699
- layer: classifyLayer(posix),
699
+ layer: classifyLayer(posix3),
700
700
  isWorkItem: isWi,
701
701
  lifecycle: isWi ? lifecycleStateOf({ status: a2.status, filePath: a2.filePath }) : void 0
702
702
  };
@@ -3914,8 +3914,8 @@ function buildOpenQuestionsReport(dir, now = /* @__PURE__ */ new Date()) {
3914
3914
  const project = loadConfig(dir)?.project.name ?? "unknown";
3915
3915
  const questions = [];
3916
3916
  let seq = 0;
3917
- for (const source of SOURCES) {
3918
- const p = join(dir, source);
3917
+ for (const source3 of SOURCES) {
3918
+ const p = join(dir, source3);
3919
3919
  if (!exists(p)) continue;
3920
3920
  let md;
3921
3921
  try {
@@ -3928,8 +3928,8 @@ function buildOpenQuestionsReport(dir, now = /* @__PURE__ */ new Date()) {
3928
3928
  const { classification, reason } = classifyQuestion(eq.text);
3929
3929
  questions.push({
3930
3930
  id: `OQ-${String(seq).padStart(3, "0")}`,
3931
- source,
3932
- sourcePath: source,
3931
+ source: source3,
3932
+ sourcePath: source3,
3933
3933
  line: eq.line,
3934
3934
  raw: eq.raw,
3935
3935
  section: "Open Questions",
@@ -4360,6 +4360,121 @@ function readPocSummary(dir) {
4360
4360
  };
4361
4361
  }
4362
4362
 
4363
+ // ../cli/src/core/poc-report.ts
4364
+ import { createHash } from "crypto";
4365
+ import matter8 from "gray-matter";
4366
+ var POC_REPORT_PREFIX = "poc-report-v";
4367
+ var SECRET_LINE = /(^|\s)(?:[A-Z][A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE_KEY)|(?:token|secret|password|api[_-]?key))\s*[:=]\s*\S+/i;
4368
+ var reportName = /^poc-report-v(\d{3,})\.md$/;
4369
+ function sha(value) {
4370
+ return createHash("sha256").update(value).digest("hex");
4371
+ }
4372
+ function posix(value) {
4373
+ return value.replace(/\\/g, "/");
4374
+ }
4375
+ function redactSecrets(value) {
4376
+ return value.split(/\r?\n/).map((line) => {
4377
+ if (!SECRET_LINE.test(line)) return line;
4378
+ return line.replace(/([:=]\s*)\S+.*/, "$1[redacted]");
4379
+ }).join("\n");
4380
+ }
4381
+ function source(dir, path9, kind) {
4382
+ const absolute = join(dir, path9);
4383
+ if (!exists(absolute)) return null;
4384
+ const raw = readFile(absolute);
4385
+ return { path: posix(path9), kind, digest: sha(raw), content: redactSecrets(raw) };
4386
+ }
4387
+ function reportFiles(dir) {
4388
+ const delivery = join(dir, "knowledge", "delivery");
4389
+ return readDir(delivery).flatMap((name) => {
4390
+ const match = name.match(reportName);
4391
+ if (!match) return [];
4392
+ try {
4393
+ const parsed = matter8(readFile(join(delivery, name)));
4394
+ const fingerprint = typeof parsed.data.source_fingerprint === "string" ? parsed.data.source_fingerprint : "";
4395
+ const manifest = parsed.data.source_manifest;
4396
+ const sourceManifest = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? Object.fromEntries(Object.entries(manifest).filter(([, digest]) => typeof digest === "string")) : {};
4397
+ return [{ path: `knowledge/delivery/${name}`, version: Number(match[1]), sourceFingerprint: fingerprint, sourceManifest }];
4398
+ } catch {
4399
+ return [];
4400
+ }
4401
+ }).sort((a2, b2) => b2.version - a2.version);
4402
+ }
4403
+ function reportPath(version) {
4404
+ return `knowledge/delivery/${POC_REPORT_PREFIX}${String(version).padStart(3, "0")}.md`;
4405
+ }
4406
+ function buildPocReportContext(dir) {
4407
+ const poc = readPocSummary(dir);
4408
+ const reports = reportFiles(dir);
4409
+ const latestReport = reports[0] ?? null;
4410
+ const nextPath = reportPath((latestReport?.version ?? 0) + 1);
4411
+ if (!poc.exists || poc.conclusion === "pending") {
4412
+ return {
4413
+ eligible: false,
4414
+ conclusion: poc.conclusion,
4415
+ status: latestReport ? "stale" : "missing",
4416
+ latestReport,
4417
+ nextPath,
4418
+ sourceFingerprint: "",
4419
+ sources: [],
4420
+ changesSinceLatest: [],
4421
+ handoff: "POC conclusion is still pending. Record validated, rejected, or inconclusive before generating a final report."
4422
+ };
4423
+ }
4424
+ const sources = [];
4425
+ const add = (path9, kind) => {
4426
+ if (sources.some((item2) => item2.path === posix(path9))) return;
4427
+ const item = source(dir, path9, kind);
4428
+ if (item) sources.push(item);
4429
+ };
4430
+ add(POC_ARTIFACT_PATH, "poc");
4431
+ const pocText = readFile(join(dir, POC_ARTIFACT_PATH));
4432
+ const knowledge = discoverKnowledge(dir);
4433
+ const workItems = discoverWorkItems(dir);
4434
+ const referencedWorkItems = workItems.filter((wi) => wi.lifecycle === "completed" && wi.id && new RegExp(`\\b${wi.id}\\b`).test(pocText));
4435
+ const resourceIds = /* @__PURE__ */ new Set();
4436
+ const decisionIds = /* @__PURE__ */ new Set();
4437
+ for (const wi of referencedWorkItems) {
4438
+ add(wi.relPath, "work-item");
4439
+ for (const resource of parseWorkItemResources(wi.rawFrontmatter)) resourceIds.add(resource.id);
4440
+ const decisions = wi.rawFrontmatter.decisions;
4441
+ if (Array.isArray(decisions)) decisions.forEach((id) => decisionIds.add(String(id)));
4442
+ }
4443
+ for (const id of resourceIds) {
4444
+ const resource = getResource(dir, id);
4445
+ if (resource) add(resource.path, "resource");
4446
+ }
4447
+ for (const artifact of knowledge) {
4448
+ if (artifact.isWorkItem || !artifact.id) continue;
4449
+ if (decisionIds.has(artifact.id) || referencedWorkItems.some((wi) => new RegExp(`\\b${artifact.id}\\b`).test(readFile(wi.filePath)))) add(artifact.relPath, "knowledge");
4450
+ }
4451
+ sources.sort((a2, b2) => a2.path.localeCompare(b2.path));
4452
+ const sourceFingerprint = sha(sources.map((item) => `${item.path}\0${item.digest}`).join("\n"));
4453
+ const status = !latestReport ? "missing" : latestReport.sourceFingerprint === sourceFingerprint ? "current" : "stale";
4454
+ const currentManifest = Object.fromEntries(sources.map((item) => [item.path, item.digest]));
4455
+ const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path9) => currentManifest[path9] !== latestReport?.sourceManifest[path9]).sort() : [];
4456
+ const sourceList = sources.map((item) => `- ${item.path} (${item.kind})`).join("\n") || "- No sources selected.";
4457
+ return {
4458
+ eligible: true,
4459
+ conclusion: poc.conclusion,
4460
+ status,
4461
+ latestReport,
4462
+ nextPath,
4463
+ sourceFingerprint,
4464
+ sources,
4465
+ changesSinceLatest,
4466
+ handoff: [
4467
+ "Create a POC Final Report from the supplied sources only. Do not explore the repository.",
4468
+ `Canonical conclusion: ${poc.conclusion}. Do not contradict it.`,
4469
+ "Use the 15 canonical sections. Mark missing evidence as Not evaluated, Not applicable, or No evidence available.",
4470
+ "Distinguish measured, calculated, estimated, and projected values. Never include secret values.",
4471
+ "",
4472
+ "Selected sources:",
4473
+ sourceList
4474
+ ].join("\n")
4475
+ };
4476
+ }
4477
+
4363
4478
  // ../cli/src/core/next-step.ts
4364
4479
  function buildDeliveryState(dir) {
4365
4480
  const wis = discoverWorkItems(dir);
@@ -4450,7 +4565,7 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
4450
4565
  if (projectMode(config) === "poc") {
4451
4566
  const poc = readPocSummary(dir);
4452
4567
  if (!poc.exists) {
4453
- return { id: "bootstrap", phase: "Setup", label: "Run `kaddo bootstrap` to create the POC knowledge baseline.", command: "kaddo bootstrap", reason: `Missing ${POC_ARTIFACT_PATH}.` };
4568
+ return { id: "poc-artifact", phase: "POC Definition", label: "Create or restore the POC artifact.", command: "kaddo project mode poc", target: POC_ARTIFACT_PATH, reason: `Missing ${POC_ARTIFACT_PATH}.` };
4454
4569
  }
4455
4570
  if (!poc.hypothesisDefined) {
4456
4571
  return { id: "poc-hypothesis", phase: "POC Definition", label: "Define the POC hypothesis.", target: POC_ARTIFACT_PATH, reason: "The POC needs a testable hypothesis before work begins." };
@@ -4460,12 +4575,9 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
4460
4575
  }
4461
4576
  const qCurrentState2 = analyzeKnowledgeArtifact(dir, CS);
4462
4577
  const qCodebase2 = analyzeKnowledgeArtifact(dir, CB);
4463
- if (qCurrentState2 !== "useful" || qCodebase2 !== "useful") {
4578
+ if (exists(join(dir, ".kaddo", "scan.json")) && (qCurrentState2 !== "useful" || qCodebase2 !== "useful")) {
4464
4579
  return { id: "poc-technical-context", phase: "POC Discovery", label: "Document the minimal technical context for the experiment.", target: qCurrentState2 !== "useful" ? CS : CB, agent: "architecture-agent", reason: "The experiment surface and constraints are not yet sufficiently documented." };
4465
4580
  }
4466
- if (!exists(join(dir, "knowledge", "resources"))) {
4467
- return { id: "poc-resources", phase: "POC Discovery", label: "Identify the project resources relevant to the experiment.", command: "kaddo resources list", reason: "No project resource catalog is available for the experiment context." };
4468
- }
4469
4581
  const delivery = buildDeliveryState(dir);
4470
4582
  if (delivery.total_work_items === 0) {
4471
4583
  return { id: "poc-create-experiment", phase: "Experiment", label: "Create the first experiment Work Item (prefer `spike`).", command: "kaddo create spike", reason: "The POC has no Work Item to produce evidence." };
@@ -4479,7 +4591,11 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
4479
4591
  if (poc.conclusion === "pending") {
4480
4592
  return { id: "poc-evaluate", phase: "Evaluation", label: "Evaluate the experiment evidence and record the POC conclusion.", target: POC_ARTIFACT_PATH, reason: "Experiment Work Items are complete but the POC conclusion is pending." };
4481
4593
  }
4482
- return { id: "poc-complete", phase: "Conclusion", label: `POC concluded: ${poc.conclusion}. Decide whether to graduate it to standard delivery.`, target: POC_ARTIFACT_PATH, reason: "The POC conclusion is recorded from experiment evidence." };
4594
+ const report = buildPocReportContext(dir);
4595
+ if (report.status !== "current") {
4596
+ return { id: "poc-final-report", phase: "Conclusion", label: `POC concluded: ${poc.conclusion}. Generate a final report if it would help communicate the result.`, command: "kaddo poc report", target: POC_ARTIFACT_PATH, reason: report.status === "stale" ? "The latest final report is stale after source changes." : "A final report is optional and has not been generated." };
4597
+ }
4598
+ return { id: "poc-complete", phase: "Conclusion", label: `POC concluded: ${poc.conclusion}. Decide whether to graduate it to standard delivery.`, target: POC_ARTIFACT_PATH, reason: "The POC conclusion and final report are current." };
4483
4599
  }
4484
4600
  const q3 = (rel) => analyzeKnowledgeArtifact(dir, rel);
4485
4601
  const resolveAgent = (agent) => {
@@ -4917,7 +5033,7 @@ function deriveDeliveryCompletedStatus(dir) {
4917
5033
  }
4918
5034
 
4919
5035
  // ../cli/src/core/assets.ts
4920
- import matter8 from "gray-matter";
5036
+ import matter9 from "gray-matter";
4921
5037
 
4922
5038
  // ../cli/src/core/version.ts
4923
5039
  import { fileURLToPath } from "url";
@@ -5593,6 +5709,50 @@ their intersection with the affected areas, and recommended actions.
5593
5709
  - The assessment is proportional \u2014 a small change gets a brief assessment.
5594
5710
  `
5595
5711
  );
5712
+ var POC_REPORT_WRITING = skill(
5713
+ "poc-report-writing",
5714
+ "POC Final Report Writing Skill",
5715
+ "delivery",
5716
+ ["implementation-agent", "work-item-agent"],
5717
+ `
5718
+ # POC Final Report Writing Skill
5719
+
5720
+ ## Purpose
5721
+
5722
+ Turn a supplied deterministic POC report context into a concise, traceable final report.
5723
+
5724
+ ## When to use
5725
+
5726
+ After a POC has a canonical conclusion and Core has prepared its deterministic report context.
5727
+
5728
+ ## Rules
5729
+
5730
+ - Use only the supplied context; do not scan the repository or infer missing results.
5731
+ - Treat \`poc.md\` as canonical for the conclusion. Never contradict it.
5732
+ - Keep facts, measurements, calculations, estimates, and recommendations distinct.
5733
+ - Never include credential values, tokens, private keys, passwords, or connection strings.
5734
+ - Use \`Not evaluated in this POC.\`, \`Not applicable.\`, or \`No evidence available.\` when sources do not support a section.
5735
+ - Return a proposal for human review; Core persists it only after confirmation.
5736
+
5737
+ ## Required structure
5738
+
5739
+ 1. Executive Summary
5740
+ 2. Problem and Objective
5741
+ 3. Hypothesis and Success Criteria
5742
+ 4. Scope, Constraints and Non-goals
5743
+ 5. Technical Approach and Architecture
5744
+ 6. Infrastructure and Project Resources
5745
+ 7. Implementation and Relevant Technical Decisions
5746
+ 8. Experiments and Validation
5747
+ 9. Results and Measurements
5748
+ 10. Cost and Efficiency Analysis
5749
+ 11. Observability and Operational Findings
5750
+ 12. Findings, Learnings, Limitations and Risks
5751
+ 13. Conclusion
5752
+ 14. Recommendation and Next Steps
5753
+ 15. Traceability and Sources
5754
+ `
5755
+ );
5596
5756
  var SKILLS = [
5597
5757
  ADR_WRITING,
5598
5758
  WORK_ITEM_REFINEMENT,
@@ -5603,10 +5763,11 @@ var SKILLS = [
5603
5763
  IMPLEMENTATION_PLANNING,
5604
5764
  MODULE_CONTEXT_REFINEMENT,
5605
5765
  EVIDENCE_VERIFICATION,
5606
- LEGACY_RISK_ASSESSMENT
5766
+ LEGACY_RISK_ASSESSMENT,
5767
+ POC_REPORT_WRITING
5607
5768
  ];
5608
5769
  var SKILL_GROUPS = {
5609
- delivery: ["work-item-refinement", "implementation-planning", "learning-capture", "evidence-verification", "legacy-risk-assessment"],
5770
+ delivery: ["work-item-refinement", "implementation-planning", "learning-capture", "evidence-verification", "legacy-risk-assessment", "poc-report-writing"],
5610
5771
  tech: ["adr-writing", "ownership-suggestion", "graph-metadata-review", "module-context-refinement"],
5611
5772
  integration: ["capsule-writing"]
5612
5773
  };
@@ -8140,7 +8301,7 @@ function canonicalSkills() {
8140
8301
  }
8141
8302
  function versionOf(content) {
8142
8303
  try {
8143
- const v = matter8(content).data.version;
8304
+ const v = matter9(content).data.version;
8144
8305
  return v === void 0 || v === null ? null : String(v);
8145
8306
  } catch {
8146
8307
  return null;
@@ -8402,9 +8563,9 @@ function parseWorkItemSource(frontmatter) {
8402
8563
  reason: "No source metadata found."
8403
8564
  };
8404
8565
  }
8405
- function renderSourceCompact(source) {
8406
- const parts = [source.type];
8407
- if (source.id) parts.push(source.id);
8566
+ function renderSourceCompact(source3) {
8567
+ const parts = [source3.type];
8568
+ if (source3.id) parts.push(source3.id);
8408
8569
  return parts.join(" \xB7 ");
8409
8570
  }
8410
8571
  function optStr2(v) {
@@ -8740,6 +8901,11 @@ var definePocHypothesis = {
8740
8901
  return { status: poc.hypothesisDefined ? "done" : "current", evidence: poc.hypothesisDefined ? [POC_ARTIFACT_PATH] : void 0, reason: poc.hypothesisDefined ? void 0 : "A POC starts with one testable hypothesis." };
8741
8902
  }
8742
8903
  };
8904
+ var ensurePocArtifact = {
8905
+ id: "poc-artifact",
8906
+ label: "Create POC artifact",
8907
+ evaluate: (ctx) => exists(join(ctx.dir, POC_ARTIFACT_PATH)) ? { status: "done", evidence: [POC_ARTIFACT_PATH] } : { status: "current", command: "kaddo project mode poc", reason: "Create the canonical POC artifact before defining the experiment." }
8908
+ };
8743
8909
  var definePocCriteria = {
8744
8910
  id: "poc-success-criteria",
8745
8911
  label: "Define success criteria",
@@ -8748,11 +8914,15 @@ var definePocCriteria = {
8748
8914
  return { status: poc.successCriteriaCount > 0 ? "done" : "pending", evidence: poc.successCriteriaCount > 0 ? [POC_ARTIFACT_PATH] : void 0, reason: poc.successCriteriaCount > 0 ? void 0 : "Define observable evidence for the hypothesis." };
8749
8915
  }
8750
8916
  };
8751
- var describePocTech = { ...describeArchitecture, id: "poc-technical-context", label: "Document minimal technical context" };
8917
+ var describePocTech = {
8918
+ id: "poc-technical-context",
8919
+ label: "Document minimal technical context",
8920
+ evaluate: (ctx) => ctx.hasScan ? describeArchitecture.evaluate(ctx) : { status: "optional", command: "kaddo scan", reason: "Add technical context only when the experiment needs to inspect existing code." }
8921
+ };
8752
8922
  var identifyPocResources = {
8753
8923
  id: "poc-resources",
8754
8924
  label: "Identify relevant resources",
8755
- evaluate: (ctx) => exists(join(ctx.dir, "knowledge/resources")) ? { status: "done", evidence: ["knowledge/resources/"] } : { status: "pending", command: "kaddo resources list" }
8925
+ evaluate: (ctx) => exists(join(ctx.dir, "knowledge/resources")) ? { status: "done", evidence: ["knowledge/resources/"] } : { status: "optional", command: "kaddo resources list", reason: "Add Project Resources only when the experiment depends on external systems." }
8756
8926
  };
8757
8927
  var createPocExperiment = {
8758
8928
  id: "poc-create-experiment",
@@ -8772,7 +8942,17 @@ var concludePoc = {
8772
8942
  return conclusion === "pending" ? { status: "pending", reason: "Record validated, rejected, or inconclusive from the evidence." } : { status: "done", evidence: [POC_ARTIFACT_PATH], reason: `Conclusion: ${conclusion}.` };
8773
8943
  }
8774
8944
  };
8775
- var POC_STEPS = [enableKaddo, bootstrapBaseline, definePocHypothesis, definePocCriteria, describePocTech, identifyPocResources, createPocExperiment, capturePocEvidence, concludePoc];
8945
+ var finalPocReport = {
8946
+ id: "poc-final-report",
8947
+ label: "Generate final report (optional)",
8948
+ evaluate: (ctx) => {
8949
+ const report = buildPocReportContext(ctx.dir);
8950
+ if (!report.eligible) return { status: "optional", reason: "Available after recording a POC conclusion." };
8951
+ if (report.status === "current") return { status: "done", evidence: [report.latestReport.path] };
8952
+ return { status: "optional", command: "kaddo poc report", reason: report.status === "stale" ? "Latest report is stale." : "A final report is optional." };
8953
+ }
8954
+ };
8955
+ var POC_STEPS = [enableKaddo, ensurePocArtifact, definePocHypothesis, definePocCriteria, describePocTech, identifyPocResources, createPocExperiment, capturePocEvidence, concludePoc, finalPocReport];
8776
8956
  function stepsForState(state) {
8777
8957
  switch (state) {
8778
8958
  case "new":
@@ -8876,6 +9056,7 @@ function mapNextStepId(id) {
8876
9056
  "poc-resources": "poc-resources",
8877
9057
  "poc-create-experiment": "poc-create-experiment",
8878
9058
  "poc-evaluate": "poc-evaluate",
9059
+ "poc-final-report": "poc-final-report",
8879
9060
  "poc-complete": "poc-evaluate"
8880
9061
  };
8881
9062
  return MAP[id] ?? id;
@@ -8890,7 +9071,7 @@ function buildProjectRoute(dir) {
8890
9071
  return { id: d3.id, label: d3.label, ...result };
8891
9072
  });
8892
9073
  for (const s of steps) {
8893
- if (s.id === ctx.nextStepId && s.status !== "done") {
9074
+ if (s.id === ctx.nextStepId && s.status !== "done" && s.status !== "optional") {
8894
9075
  s.status = "current";
8895
9076
  }
8896
9077
  }
@@ -8902,8 +9083,8 @@ function buildProjectRoute(dir) {
8902
9083
  break;
8903
9084
  }
8904
9085
  }
8905
- const completed = steps.filter((s) => s.status === "done").length;
8906
- const total = steps.length;
9086
+ const completed = steps.filter((s) => s.status === "done" && s.status !== "optional").length;
9087
+ const total = steps.filter((s) => s.status !== "optional").length;
8907
9088
  const progressPercent = total > 0 ? Math.round(completed / total * 100) : 0;
8908
9089
  return {
8909
9090
  type: state,
@@ -8960,7 +9141,7 @@ function renderRouteCompact(route) {
8960
9141
  }
8961
9142
 
8962
9143
  // ../cli/src/core/metadata-health.ts
8963
- import matter9 from "gray-matter";
9144
+ import matter10 from "gray-matter";
8964
9145
  var KNOWLEDGE_FILES2 = [
8965
9146
  "knowledge/business/business.md",
8966
9147
  "knowledge/product/product.md",
@@ -8980,7 +9161,7 @@ function analyzeMetadataHealth(dir) {
8980
9161
  let data;
8981
9162
  try {
8982
9163
  const raw = readFile(p);
8983
- data = matter9(raw).data;
9164
+ data = matter10(raw).data;
8984
9165
  } catch {
8985
9166
  continue;
8986
9167
  }
@@ -9012,7 +9193,7 @@ function analyzeMetadataHealth(dir) {
9012
9193
  if (isFile(p) && entry.endsWith(".md")) {
9013
9194
  try {
9014
9195
  const raw = readFile(p);
9015
- const fm = matter9(raw).data;
9196
+ const fm = matter10(raw).data;
9016
9197
  if (fm.status === "done") {
9017
9198
  const rel = p.replace(dir + "/", "").replace(dir + "\\", "").replace(/\\/g, "/");
9018
9199
  findings.push({
@@ -9967,7 +10148,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
9967
10148
  }
9968
10149
  }
9969
10150
  try {
9970
- const body = matter10(readFile(wi.filePath)).content;
10151
+ const body = matter11(readFile(wi.filePath)).content;
9971
10152
  if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
9972
10153
  else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
9973
10154
  if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
@@ -10375,7 +10556,7 @@ function parseRatioNumerator(s) {
10375
10556
  }
10376
10557
  var SAVINGS_DISCLAIMER = "These are evidence-based estimates, not exact ROI. Adjust assumptions with real team data.";
10377
10558
  function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10378
- const { assumptions: a2, source } = loadAssumptions(dir);
10559
+ const { assumptions: a2, source: source3 } = loadAssumptions(dir);
10379
10560
  const impact = buildImpactReport(dir, { scope: opts.scope, scopeSource: opts.scopeSource }, now);
10380
10561
  const completed = impact.traceability.completed_work_items;
10381
10562
  const wiWithOwnership = parseRatioNumerator(impact.traceability.work_items_connected_to_code);
@@ -10415,7 +10596,7 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10415
10596
  if (impact.score === null || impact.score < 60 || graphQuality === "empty") {
10416
10597
  level = "Low";
10417
10598
  reasons.push("Impact score is low or the knowledge graph is empty.");
10418
- } else if (impact.score >= 85 && graphQuality === "good" && source === "file" && guard.available && guard.resolved > 0) {
10599
+ } else if (impact.score >= 85 && graphQuality === "good" && source3 === "file" && guard.available && guard.resolved > 0) {
10419
10600
  level = "High";
10420
10601
  reasons.push("Strong evidence, custom assumptions and recorded drift resolution.");
10421
10602
  } else {
@@ -10429,9 +10610,9 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10429
10610
  } else {
10430
10611
  reasons.push("Guard history available, but no resolved drift warnings have been recorded yet.");
10431
10612
  }
10432
- reasons.push(source === "file" ? "Assumptions come from `.kaddo/savings.yml`." : "Savings assumptions are defaults and should be calibrated.");
10613
+ reasons.push(source3 === "file" ? "Assumptions come from `.kaddo/savings.yml`." : "Savings assumptions are defaults and should be calibrated.");
10433
10614
  const actions = [];
10434
- if (source === "default") actions.push("Run `kaddo savings init` to customize assumptions.");
10615
+ if (source3 === "default") actions.push("Run `kaddo savings init` to customize assumptions.");
10435
10616
  actions.push("Calibrate hourly cost and team assumptions with real team data.");
10436
10617
  actions.push("Run `kaddo impact` to verify the underlying evidence quality.");
10437
10618
  actions.push("Use this report as directional evidence, not accounting data.");
@@ -10449,7 +10630,7 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10449
10630
  scope: impact.scope,
10450
10631
  currency: a2.currency,
10451
10632
  disclaimer: SAVINGS_DISCLAIMER,
10452
- assumptions_source: source,
10633
+ assumptions_source: source3,
10453
10634
  assumptions: {
10454
10635
  hourly_cost: a2.hourly_cost,
10455
10636
  context_preparation_minutes_saved_per_work_item: a2.context_preparation_minutes_saved_per_work_item,
@@ -10680,7 +10861,7 @@ function serializeDriftJson(r2) {
10680
10861
  }
10681
10862
 
10682
10863
  // src/workitems.ts
10683
- import matter11 from "gray-matter";
10864
+ import matter12 from "gray-matter";
10684
10865
  var VALID_SOURCES2 = /* @__PURE__ */ new Set(["manual", "roadmap", "jira", "github", "notion", "xlsx", "csv", "api", "external", "unknown"]);
10685
10866
  function optStr3(v) {
10686
10867
  return typeof v === "string" && v.trim() ? v.trim() : void 0;
@@ -10771,7 +10952,7 @@ function listWorkItems(root) {
10771
10952
  if (raw === null) continue;
10772
10953
  let parsed;
10773
10954
  try {
10774
- parsed = matter11(raw);
10955
+ parsed = matter12(raw);
10775
10956
  } catch {
10776
10957
  continue;
10777
10958
  }
@@ -10780,8 +10961,8 @@ function listWorkItems(root) {
10780
10961
  const status = lifecycleOf(data, rel);
10781
10962
  const code = strArray2(data.code);
10782
10963
  const domains = strArray2(data.domains);
10783
- const source = parseSource(data);
10784
- const readiness = status === "draft" ? assessReadiness(data, parsed.content, code, domains, source) : void 0;
10964
+ const source3 = parseSource(data);
10965
+ const readiness = status === "draft" ? assessReadiness(data, parsed.content, code, domains, source3) : void 0;
10785
10966
  items.push({
10786
10967
  id: String(data.id ?? "") || rel,
10787
10968
  title: String(data.title ?? ""),
@@ -10794,7 +10975,7 @@ function listWorkItems(root) {
10794
10975
  capabilities: strArray2(data.capabilities),
10795
10976
  decisions: strArray2(data.decisions),
10796
10977
  capsules: strArray2(data.capsules),
10797
- source,
10978
+ source: source3,
10798
10979
  domains,
10799
10980
  readiness,
10800
10981
  ...data.implementation_status ? { implementation_status: String(data.implementation_status) } : {},
@@ -10811,7 +10992,7 @@ function listWorkItems(root) {
10811
10992
  }
10812
10993
  return items.sort((a2, b2) => a2.id.localeCompare(b2.id));
10813
10994
  }
10814
- function assessReadiness(data, content, code, domains, source) {
10995
+ function assessReadiness(data, content, code, domains, source3) {
10815
10996
  const body = content.trim();
10816
10997
  const lower = body.toLowerCase();
10817
10998
  const warnings = [];
@@ -10965,9 +11146,28 @@ var RESOURCES = [
10965
11146
  root,
10966
11147
  "kaddo://poc",
10967
11148
  "knowledge/delivery/poc.md",
10968
- "POC artifact not found. Set `project.mode: poc` and run `kaddo bootstrap` first."
11149
+ "POC artifact not found. Initialize in POC mode or switch with `kaddo project mode poc`."
10969
11150
  )
10970
11151
  },
11152
+ {
11153
+ uri: "kaddo://poc-report",
11154
+ name: "Kaddo final POC report",
11155
+ description: "Latest final POC report, or deterministic report status and source selection.",
11156
+ mimeType: "text/markdown",
11157
+ read: (root) => {
11158
+ const context = buildPocReportContext(root);
11159
+ if (context.latestReport) {
11160
+ const saved = readText(root, context.latestReport.path);
11161
+ if (saved) return text2("kaddo://poc-report", saved);
11162
+ }
11163
+ const lines = [
11164
+ `Final POC report: ${context.status}`,
11165
+ `Conclusion: ${context.conclusion}`,
11166
+ context.handoff
11167
+ ];
11168
+ return text2("kaddo://poc-report", lines.join("\n\n"));
11169
+ }
11170
+ },
10971
11171
  {
10972
11172
  uri: "kaddo://capsules",
10973
11173
  name: "Kaddo knowledge capsules",
@@ -11251,14 +11451,14 @@ function getPrompt(root, name) {
11251
11451
  }
11252
11452
 
11253
11453
  // src/tools.ts
11254
- import matter15 from "gray-matter";
11454
+ import matter16 from "gray-matter";
11255
11455
 
11256
11456
  // ../cli/dist/core.js
11257
11457
  import fs3 from "fs";
11258
11458
  import path3 from "path";
11259
11459
  import { z as z3 } from "zod";
11260
11460
  import { parse as parseYaml10, stringify as stringifyYaml5 } from "yaml";
11261
- import matter12 from "gray-matter";
11461
+ import matter13 from "gray-matter";
11262
11462
  import matter22 from "gray-matter";
11263
11463
  import { parse as parseYaml62, stringify as stringifyYaml42 } from "yaml";
11264
11464
  import { parse as parseYaml22 } from "yaml";
@@ -11269,25 +11469,27 @@ import matter32 from "gray-matter";
11269
11469
  import matter42 from "gray-matter";
11270
11470
  import matter52 from "gray-matter";
11271
11471
  import { parse as parseYaml72 } from "yaml";
11472
+ import { createHash as createHash2 } from "crypto";
11272
11473
  import matter62 from "gray-matter";
11474
+ import matter72 from "gray-matter";
11273
11475
  import { fileURLToPath as fileURLToPath2 } from "url";
11274
11476
  import { dirname as dirname2, join as join22, parse as parse2 } from "path";
11275
11477
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
11276
- import matter72 from "gray-matter";
11277
11478
  import matter82 from "gray-matter";
11278
11479
  import matter92 from "gray-matter";
11480
+ import matter102 from "gray-matter";
11279
11481
  import { parse as parseYaml82, stringify as stringifyYaml52 } from "yaml";
11280
11482
  import fs4 from "fs";
11281
11483
  import path4 from "path";
11282
11484
  import crypto2 from "crypto";
11283
- import matter102 from "gray-matter";
11485
+ import matter112 from "gray-matter";
11284
11486
  import crypto3 from "crypto";
11285
11487
  import fs5 from "fs";
11286
- import matter112 from "gray-matter";
11287
11488
  import matter122 from "gray-matter";
11489
+ import matter132 from "gray-matter";
11288
11490
  import { parse as parseYaml92, stringify as stringifyYaml6 } from "yaml";
11289
11491
  import { parse as parseYaml102, stringify as stringifyYaml7 } from "yaml";
11290
- import matter13 from "gray-matter";
11492
+ import matter14 from "gray-matter";
11291
11493
  function exists2(p) {
11292
11494
  return fs3.existsSync(p);
11293
11495
  }
@@ -11463,7 +11665,7 @@ function parseImpactAnalysis2(data) {
11463
11665
  }
11464
11666
  function parseArtifact2(filePath, raw) {
11465
11667
  try {
11466
- const { data } = matter12(raw);
11668
+ const { data } = matter13(raw);
11467
11669
  const globs = Array.isArray(data.code) ? data.code.filter(Boolean) : [];
11468
11670
  return {
11469
11671
  filePath,
@@ -11577,12 +11779,12 @@ function classifyLayer2(posixPath) {
11577
11779
  return "unknown";
11578
11780
  }
11579
11781
  function enrich2(dir, a2) {
11580
- const posix = toPosix7(a2.filePath);
11782
+ const posix22 = toPosix7(a2.filePath);
11581
11783
  const isWi = isWorkItemArtifact2(a2);
11582
11784
  return {
11583
11785
  ...a2,
11584
11786
  relPath: relativeTo2(dir, a2.filePath),
11585
- layer: classifyLayer2(posix),
11787
+ layer: classifyLayer2(posix22),
11586
11788
  isWorkItem: isWi,
11587
11789
  lifecycle: isWi ? lifecycleStateOf2({ status: a2.status, filePath: a2.filePath }) : void 0
11588
11790
  };
@@ -13612,6 +13814,177 @@ function buildTechDecisions2(dir) {
13612
13814
  function hasUnmaterializedDecisions(td) {
13613
13815
  return td.candidates > 0 && td.adrs === 0;
13614
13816
  }
13817
+ var POC_ARTIFACT_PATH2 = "knowledge/delivery/poc.md";
13818
+ var PLACEHOLDER2 = /^[_<].*[_>]$|^(describe|list|document|what|how)\b/i;
13819
+ function section3(markdown, heading) {
13820
+ const match = markdown.match(new RegExp(`^##\\s+${heading}\\s*$([\\s\\S]*?)(?=^##\\s+|(?![\\s\\S]))`, "im"));
13821
+ return match?.[1] ?? "";
13822
+ }
13823
+ function meaningful2(text22) {
13824
+ return text22.split(/\r?\n/).some((line) => {
13825
+ const value = line.trim().replace(/^[-*]\s+(?:\[[^\]]+\]\s*)?/, "");
13826
+ return Boolean(value) && !PLACEHOLDER2.test(value) && !/^status:\s*(pending|tbd)$/i.test(value);
13827
+ });
13828
+ }
13829
+ function readPocSummary2(dir) {
13830
+ const path52 = join3(dir, POC_ARTIFACT_PATH2);
13831
+ if (!exists2(path52)) {
13832
+ return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
13833
+ }
13834
+ let markdown = "";
13835
+ try {
13836
+ markdown = readFile2(path52);
13837
+ } catch {
13838
+ return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
13839
+ }
13840
+ const criteria = section3(markdown, "Success Criteria").split(/\r?\n/).filter((line) => /^\s*[-*]\s+(?:\[[ xX]\]\s+)?/.test(line)).filter((line) => meaningful2(line)).length;
13841
+ const conclusionBody = section3(markdown, "Conclusion");
13842
+ const conclusionMatch = conclusionBody.match(/\b(?:status|outcome)\s*:\s*(validated|rejected|inconclusive)\b/i);
13843
+ return {
13844
+ exists: true,
13845
+ hypothesisDefined: meaningful2(section3(markdown, "Hypothesis")),
13846
+ successCriteriaCount: criteria,
13847
+ constraintsDefined: meaningful2(section3(markdown, "Constraints")),
13848
+ nonGoalsDefined: meaningful2(section3(markdown, "Non-goals")),
13849
+ conclusion: conclusionMatch ? conclusionMatch[1].toLowerCase() : "pending"
13850
+ };
13851
+ }
13852
+ var POC_REPORT_PREFIX2 = "poc-report-v";
13853
+ var SECRET_LINE2 = /(^|\s)(?:[A-Z][A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE_KEY)|(?:token|secret|password|api[_-]?key))\s*[:=]\s*\S+/i;
13854
+ var reportName2 = /^poc-report-v(\d{3,})\.md$/;
13855
+ function sha2(value) {
13856
+ return createHash2("sha256").update(value).digest("hex");
13857
+ }
13858
+ function posix2(value) {
13859
+ return value.replace(/\\/g, "/");
13860
+ }
13861
+ function redactSecrets2(value) {
13862
+ return value.split(/\r?\n/).map((line) => {
13863
+ if (!SECRET_LINE2.test(line)) return line;
13864
+ return line.replace(/([:=]\s*)\S+.*/, "$1[redacted]");
13865
+ }).join("\n");
13866
+ }
13867
+ function source2(dir, path52, kind) {
13868
+ const absolute = join3(dir, path52);
13869
+ if (!exists2(absolute)) return null;
13870
+ const raw = readFile2(absolute);
13871
+ return { path: posix2(path52), kind, digest: sha2(raw), content: redactSecrets2(raw) };
13872
+ }
13873
+ function reportFiles2(dir) {
13874
+ const delivery = join3(dir, "knowledge", "delivery");
13875
+ return readDir2(delivery).flatMap((name) => {
13876
+ const match = name.match(reportName2);
13877
+ if (!match) return [];
13878
+ try {
13879
+ const parsed = matter62(readFile2(join3(delivery, name)));
13880
+ const fingerprint = typeof parsed.data.source_fingerprint === "string" ? parsed.data.source_fingerprint : "";
13881
+ const manifest = parsed.data.source_manifest;
13882
+ const sourceManifest = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? Object.fromEntries(Object.entries(manifest).filter(([, digest]) => typeof digest === "string")) : {};
13883
+ return [{ path: `knowledge/delivery/${name}`, version: Number(match[1]), sourceFingerprint: fingerprint, sourceManifest }];
13884
+ } catch {
13885
+ return [];
13886
+ }
13887
+ }).sort((a2, b2) => b2.version - a2.version);
13888
+ }
13889
+ function reportPath2(version) {
13890
+ return `knowledge/delivery/${POC_REPORT_PREFIX2}${String(version).padStart(3, "0")}.md`;
13891
+ }
13892
+ function buildPocReportContext2(dir) {
13893
+ const poc = readPocSummary2(dir);
13894
+ const reports = reportFiles2(dir);
13895
+ const latestReport = reports[0] ?? null;
13896
+ const nextPath = reportPath2((latestReport?.version ?? 0) + 1);
13897
+ if (!poc.exists || poc.conclusion === "pending") {
13898
+ return {
13899
+ eligible: false,
13900
+ conclusion: poc.conclusion,
13901
+ status: latestReport ? "stale" : "missing",
13902
+ latestReport,
13903
+ nextPath,
13904
+ sourceFingerprint: "",
13905
+ sources: [],
13906
+ changesSinceLatest: [],
13907
+ handoff: "POC conclusion is still pending. Record validated, rejected, or inconclusive before generating a final report."
13908
+ };
13909
+ }
13910
+ const sources = [];
13911
+ const add = (path52, kind) => {
13912
+ if (sources.some((item2) => item2.path === posix2(path52))) return;
13913
+ const item = source2(dir, path52, kind);
13914
+ if (item) sources.push(item);
13915
+ };
13916
+ add(POC_ARTIFACT_PATH2, "poc");
13917
+ const pocText = readFile2(join3(dir, POC_ARTIFACT_PATH2));
13918
+ const knowledge = discoverKnowledge2(dir);
13919
+ const workItems = discoverWorkItems2(dir);
13920
+ const referencedWorkItems = workItems.filter((wi) => wi.lifecycle === "completed" && wi.id && new RegExp(`\\b${wi.id}\\b`).test(pocText));
13921
+ const resourceIds = /* @__PURE__ */ new Set();
13922
+ const decisionIds = /* @__PURE__ */ new Set();
13923
+ for (const wi of referencedWorkItems) {
13924
+ add(wi.relPath, "work-item");
13925
+ for (const resource of parseWorkItemResources2(wi.rawFrontmatter)) resourceIds.add(resource.id);
13926
+ const decisions = wi.rawFrontmatter.decisions;
13927
+ if (Array.isArray(decisions)) decisions.forEach((id) => decisionIds.add(String(id)));
13928
+ }
13929
+ for (const id of resourceIds) {
13930
+ const resource = getResource2(dir, id);
13931
+ if (resource) add(resource.path, "resource");
13932
+ }
13933
+ for (const artifact of knowledge) {
13934
+ if (artifact.isWorkItem || !artifact.id) continue;
13935
+ if (decisionIds.has(artifact.id) || referencedWorkItems.some((wi) => new RegExp(`\\b${artifact.id}\\b`).test(readFile2(wi.filePath)))) add(artifact.relPath, "knowledge");
13936
+ }
13937
+ sources.sort((a2, b2) => a2.path.localeCompare(b2.path));
13938
+ const sourceFingerprint = sha2(sources.map((item) => `${item.path}\0${item.digest}`).join("\n"));
13939
+ const status = !latestReport ? "missing" : latestReport.sourceFingerprint === sourceFingerprint ? "current" : "stale";
13940
+ const currentManifest = Object.fromEntries(sources.map((item) => [item.path, item.digest]));
13941
+ const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path52) => currentManifest[path52] !== latestReport?.sourceManifest[path52]).sort() : [];
13942
+ const sourceList = sources.map((item) => `- ${item.path} (${item.kind})`).join("\n") || "- No sources selected.";
13943
+ return {
13944
+ eligible: true,
13945
+ conclusion: poc.conclusion,
13946
+ status,
13947
+ latestReport,
13948
+ nextPath,
13949
+ sourceFingerprint,
13950
+ sources,
13951
+ changesSinceLatest,
13952
+ handoff: [
13953
+ "Create a POC Final Report from the supplied sources only. Do not explore the repository.",
13954
+ `Canonical conclusion: ${poc.conclusion}. Do not contradict it.`,
13955
+ "Use the 15 canonical sections. Mark missing evidence as Not evaluated, Not applicable, or No evidence available.",
13956
+ "Distinguish measured, calculated, estimated, and projected values. Never include secret values.",
13957
+ "",
13958
+ "Selected sources:",
13959
+ sourceList
13960
+ ].join("\n")
13961
+ };
13962
+ }
13963
+ function canonicalReport(context, proposal) {
13964
+ const supersedes = context.latestReport?.path.split("/").pop();
13965
+ const data = {
13966
+ type: "poc-report",
13967
+ report_version: Number(context.nextPath.match(/v(\d+)\.md$/)?.[1] ?? 1),
13968
+ generated_at: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
13969
+ poc_conclusion: context.conclusion,
13970
+ source_fingerprint: context.sourceFingerprint,
13971
+ sources: context.sources.map((item) => item.path),
13972
+ source_manifest: Object.fromEntries(context.sources.map((item) => [item.path, item.digest]))
13973
+ };
13974
+ if (supersedes) data.supersedes = supersedes;
13975
+ return matter62.stringify(redactSecrets2(proposal).trimEnd() + "\n", data);
13976
+ }
13977
+ function persistPocReport(dir, proposal, opts = {}) {
13978
+ const context = buildPocReportContext2(dir);
13979
+ if (!context.eligible) throw new Error(context.handoff);
13980
+ if (context.status === "current" && !opts.force) return { status: "current", path: context.latestReport.path, message: "The current POC report already represents the latest evidence." };
13981
+ if (!opts.confirm) return { status: "needs_confirmation", path: context.nextPath, content: canonicalReport(context, proposal), message: "Review the proposed report and confirm persistence." };
13982
+ const output = join3(dir, context.nextPath);
13983
+ if (exists2(output)) throw new Error(`POC report already exists: ${context.nextPath}`);
13984
+ const content = canonicalReport(context, proposal);
13985
+ writeFile2(output, content);
13986
+ return { status: "persisted", path: context.nextPath, content };
13987
+ }
13615
13988
  function resolveVersion2() {
13616
13989
  try {
13617
13990
  let dir = dirname2(fileURLToPath2(import.meta.url));
@@ -14280,6 +14653,50 @@ their intersection with the affected areas, and recommended actions.
14280
14653
  - The assessment is proportional \u2014 a small change gets a brief assessment.
14281
14654
  `
14282
14655
  );
14656
+ var POC_REPORT_WRITING2 = skill2(
14657
+ "poc-report-writing",
14658
+ "POC Final Report Writing Skill",
14659
+ "delivery",
14660
+ ["implementation-agent", "work-item-agent"],
14661
+ `
14662
+ # POC Final Report Writing Skill
14663
+
14664
+ ## Purpose
14665
+
14666
+ Turn a supplied deterministic POC report context into a concise, traceable final report.
14667
+
14668
+ ## When to use
14669
+
14670
+ After a POC has a canonical conclusion and Core has prepared its deterministic report context.
14671
+
14672
+ ## Rules
14673
+
14674
+ - Use only the supplied context; do not scan the repository or infer missing results.
14675
+ - Treat \`poc.md\` as canonical for the conclusion. Never contradict it.
14676
+ - Keep facts, measurements, calculations, estimates, and recommendations distinct.
14677
+ - Never include credential values, tokens, private keys, passwords, or connection strings.
14678
+ - Use \`Not evaluated in this POC.\`, \`Not applicable.\`, or \`No evidence available.\` when sources do not support a section.
14679
+ - Return a proposal for human review; Core persists it only after confirmation.
14680
+
14681
+ ## Required structure
14682
+
14683
+ 1. Executive Summary
14684
+ 2. Problem and Objective
14685
+ 3. Hypothesis and Success Criteria
14686
+ 4. Scope, Constraints and Non-goals
14687
+ 5. Technical Approach and Architecture
14688
+ 6. Infrastructure and Project Resources
14689
+ 7. Implementation and Relevant Technical Decisions
14690
+ 8. Experiments and Validation
14691
+ 9. Results and Measurements
14692
+ 10. Cost and Efficiency Analysis
14693
+ 11. Observability and Operational Findings
14694
+ 12. Findings, Learnings, Limitations and Risks
14695
+ 13. Conclusion
14696
+ 14. Recommendation and Next Steps
14697
+ 15. Traceability and Sources
14698
+ `
14699
+ );
14283
14700
  var SKILLS2 = [
14284
14701
  ADR_WRITING2,
14285
14702
  WORK_ITEM_REFINEMENT2,
@@ -14290,10 +14707,11 @@ var SKILLS2 = [
14290
14707
  IMPLEMENTATION_PLANNING2,
14291
14708
  MODULE_CONTEXT_REFINEMENT2,
14292
14709
  EVIDENCE_VERIFICATION2,
14293
- LEGACY_RISK_ASSESSMENT2
14710
+ LEGACY_RISK_ASSESSMENT2,
14711
+ POC_REPORT_WRITING2
14294
14712
  ];
14295
14713
  var SKILL_GROUPS2 = {
14296
- delivery: ["work-item-refinement", "implementation-planning", "learning-capture", "evidence-verification", "legacy-risk-assessment"],
14714
+ delivery: ["work-item-refinement", "implementation-planning", "learning-capture", "evidence-verification", "legacy-risk-assessment", "poc-report-writing"],
14297
14715
  tech: ["adr-writing", "ownership-suggestion", "graph-metadata-review", "module-context-refinement"],
14298
14716
  integration: ["capsule-writing"]
14299
14717
  };
@@ -17138,7 +17556,6 @@ var LEGACY_STEPS2 = [
17138
17556
  runGuard2,
17139
17557
  captureLearning2
17140
17558
  ];
17141
- var describePocTech2 = { ...describeArchitecture2, id: "poc-technical-context", label: "Document minimal technical context" };
17142
17559
  var USER_FACING_KEYWORDS_EN = [
17143
17560
  "user",
17144
17561
  "visitor",
@@ -17542,7 +17959,7 @@ function parseOriginalSnapshot(fm) {
17542
17959
  function readBody(filePath) {
17543
17960
  try {
17544
17961
  const raw = readFile2(filePath);
17545
- return matter92(raw).content;
17962
+ return matter102(raw).content;
17546
17963
  } catch {
17547
17964
  return "";
17548
17965
  }
@@ -17827,7 +18244,7 @@ function ensureH1(body, title) {
17827
18244
  ${trimmed}`;
17828
18245
  }
17829
18246
  function serialize2(data, body) {
17830
- return matter102.stringify(`
18247
+ return matter112.stringify(`
17831
18248
  ${body.trim()}
17832
18249
  `, data);
17833
18250
  }
@@ -17862,7 +18279,7 @@ function createWorkItem(dir, opts) {
17862
18279
  const title = intent.split(/\r?\n/)[0].trim().slice(0, 120);
17863
18280
  const today22 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
17864
18281
  const summary = opts.summary?.trim() || intent;
17865
- const source = opts.source ? { ...opts.source, inferred: false } : { type: "manual", inferred: false };
18282
+ const source22 = opts.source ? { ...opts.source, inferred: false } : { type: "manual", inferred: false };
17866
18283
  const data = {
17867
18284
  type,
17868
18285
  id,
@@ -17870,7 +18287,7 @@ function createWorkItem(dir, opts) {
17870
18287
  status: "draft",
17871
18288
  work_type: type,
17872
18289
  created_at: today22,
17873
- source,
18290
+ source: source22,
17874
18291
  generated_by: "kaddo-admin",
17875
18292
  affected_modules: [],
17876
18293
  summary
@@ -18618,7 +19035,7 @@ function detectFormat(raw) {
18618
19035
  if (!trimmed) throw new ImportError("EMPTY_INPUT", "Import content is empty.");
18619
19036
  if (trimmed.startsWith("---")) {
18620
19037
  try {
18621
- const { data } = matter112(trimmed);
19038
+ const { data } = matter122(trimmed);
18622
19039
  if (data && typeof data === "object" && Object.keys(data).length > 0) {
18623
19040
  const d3 = data;
18624
19041
  if (d3.type === "work-item" || d3.id && typeof d3.id === "string" && /^WI-\d+/.test(d3.id)) {
@@ -18640,7 +19057,7 @@ function parseContent(raw) {
18640
19057
  let bodyText = trimmed;
18641
19058
  if (format === "kaddo-frontmatter" || format === "markdown-frontmatter") {
18642
19059
  try {
18643
- const parsed = matter112(trimmed);
19060
+ const parsed = matter122(trimmed);
18644
19061
  frontmatterFields = parsed.data ?? {};
18645
19062
  bodyText = parsed.content.trim();
18646
19063
  } catch {
@@ -18712,12 +19129,12 @@ function normalizeImportFields(parsed, opts) {
18712
19129
  const intent = parsed.title;
18713
19130
  const body = hasRealBody ? parsed.body : void 0;
18714
19131
  const summary = hasRealBody ? deriveShortSummary(parsed) : parsed.title;
18715
- const source = {
19132
+ const source22 = {
18716
19133
  type: opts.importSource === "chat" ? "chat" : "external",
18717
19134
  imported_at: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
18718
19135
  };
18719
- source.source_format = opts.sourceFormat;
18720
- source.source_hash = opts.sourceHash;
19136
+ source22.source_format = opts.sourceFormat;
19137
+ source22.source_hash = opts.sourceHash;
18721
19138
  const snapshot = { title: parsed.title };
18722
19139
  if (summary !== parsed.title) snapshot.description = summary;
18723
19140
  if (typeof parsed.candidateFields.type === "string") snapshot.type = parsed.candidateFields.type;
@@ -18726,7 +19143,7 @@ function normalizeImportFields(parsed, opts) {
18726
19143
  const labels = parsed.candidateFields.labels.filter((l2) => typeof l2 === "string");
18727
19144
  if (labels.length > 0) snapshot.labels = labels;
18728
19145
  }
18729
- return { type, intent, summary, body, source, snapshot, discardedFields };
19146
+ return { type, intent, summary, body, source: source22, snapshot, discardedFields };
18730
19147
  }
18731
19148
  function computeContentHash(raw) {
18732
19149
  return crypto3.createHash("sha256").update(raw.trim(), "utf-8").digest("hex");
@@ -18737,10 +19154,10 @@ function findDuplicateByHash(dir, hash) {
18737
19154
  const raw = readFile2(item.filePath);
18738
19155
  if (!raw) continue;
18739
19156
  try {
18740
- const { data } = matter112(raw);
18741
- const source = data?.source;
18742
- if (source && typeof source === "object" && !Array.isArray(source)) {
18743
- const s = source;
19157
+ const { data } = matter122(raw);
19158
+ const source22 = data?.source;
19159
+ if (source22 && typeof source22 === "object" && !Array.isArray(source22)) {
19160
+ const s = source22;
18744
19161
  if (s.source_hash === hash) {
18745
19162
  return { workItemId: data.id ?? item.id ?? item.title, path: item.relPath };
18746
19163
  }
@@ -19632,7 +20049,7 @@ function buildInitiativeContent(id, input) {
19632
20049
  if (input.priority) data.priority = input.priority;
19633
20050
  if (input.source) data.source = input.source;
19634
20051
  if (input.sourceId) data.source_id = input.sourceId;
19635
- return matter13.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
20052
+ return matter14.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
19636
20053
  }
19637
20054
  function createInitiative(dir, input) {
19638
20055
  if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
@@ -19656,10 +20073,10 @@ function transitionInitiative(dir, id, to) {
19656
20073
  throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
19657
20074
  }
19658
20075
  const raw = readFile2(ini.filePath);
19659
- const { data, content } = matter13(raw, {});
20076
+ const { data, content } = matter14(raw, {});
19660
20077
  data.status = to;
19661
20078
  if (to === "completed") data.completed_at = today2();
19662
- const newContent = matter13.stringify(content, data);
20079
+ const newContent = matter14.stringify(content, data);
19663
20080
  writeFile2(ini.filePath, newContent);
19664
20081
  const updated = parseInitiative2(dir, ini.filePath, newContent);
19665
20082
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
@@ -19669,7 +20086,7 @@ function addCandidate(dir, id, candidate) {
19669
20086
  const ini = getInitiative(dir, id);
19670
20087
  if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
19671
20088
  const raw = readFile2(ini.filePath);
19672
- const { data, content } = matter13(raw, {});
20089
+ const { data, content } = matter14(raw, {});
19673
20090
  const existing = Array.isArray(data.candidates) ? data.candidates : [];
19674
20091
  const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
19675
20092
  const normalized = normalizeCandidate(candidate, fallbackId);
@@ -19677,7 +20094,7 @@ function addCandidate(dir, id, candidate) {
19677
20094
  throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
19678
20095
  }
19679
20096
  data.candidates = [...existing, candidateToData(normalized)];
19680
- const newContent = matter13.stringify(content, data);
20097
+ const newContent = matter14.stringify(content, data);
19681
20098
  writeFile2(ini.filePath, newContent);
19682
20099
  const updated = parseInitiative2(dir, ini.filePath, newContent);
19683
20100
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
@@ -19687,7 +20104,7 @@ function updateInitiative(dir, id, patch) {
19687
20104
  const ini = getInitiative(dir, id);
19688
20105
  if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
19689
20106
  const raw = readFile2(ini.filePath);
19690
- const { data, content } = matter13(raw, {});
20107
+ const { data, content } = matter14(raw, {});
19691
20108
  if (patch.title !== void 0) {
19692
20109
  if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
19693
20110
  data.title = patch.title.trim();
@@ -19697,7 +20114,7 @@ function updateInitiative(dir, id, patch) {
19697
20114
  if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
19698
20115
  if (patch.domains !== void 0) data.domains = patch.domains;
19699
20116
  if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
19700
- const newContent = matter13.stringify(content, data);
20117
+ const newContent = matter14.stringify(content, data);
19701
20118
  writeFile2(ini.filePath, newContent);
19702
20119
  const updated = parseInitiative2(dir, ini.filePath, newContent);
19703
20120
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
@@ -19715,10 +20132,10 @@ function addExternalLink(dir, id, link) {
19715
20132
  throw new InitiativeWriteError(`External link ${link.integration}:${link.externalId} already exists on ${id}.`);
19716
20133
  }
19717
20134
  const raw = readFile2(ini.filePath);
19718
- const { data, content } = matter13(raw, {});
20135
+ const { data, content } = matter14(raw, {});
19719
20136
  const existing = Array.isArray(data.external_links) ? data.external_links : [];
19720
20137
  data.external_links = [...existing, externalLinkToData(link)];
19721
- const newContent = matter13.stringify(content, data);
20138
+ const newContent = matter14.stringify(content, data);
19722
20139
  writeFile2(ini.filePath, newContent);
19723
20140
  const updated = parseInitiative2(dir, ini.filePath, newContent);
19724
20141
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding an external link.");
@@ -19787,7 +20204,7 @@ function buildWorkItemContent(wiId, iniId, candidate) {
19787
20204
  "_What did we learn? Update after completion._",
19788
20205
  ""
19789
20206
  ].join("\n");
19790
- return matter13.stringify("\n" + body, data);
20207
+ return matter14.stringify("\n" + body, data);
19791
20208
  }
19792
20209
  function materializeCandidate(dir, id, candidateId) {
19793
20210
  const ini = getInitiative(dir, id);
@@ -19804,13 +20221,13 @@ function materializeCandidate(dir, id, candidateId) {
19804
20221
  ensureDir2(join3(dir, DRAFT_DIR));
19805
20222
  writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
19806
20223
  const raw = readFile2(ini.filePath);
19807
- const { data, content } = matter13(raw, {});
20224
+ const { data, content } = matter14(raw, {});
19808
20225
  const cands = Array.isArray(data.candidates) ? data.candidates : [];
19809
20226
  for (const c of cands) {
19810
20227
  if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
19811
20228
  }
19812
20229
  data.candidates = cands;
19813
- const newContent = matter13.stringify(content, data);
20230
+ const newContent = matter14.stringify(content, data);
19814
20231
  writeFile2(ini.filePath, newContent);
19815
20232
  const updated = parseInitiative2(dir, ini.filePath, newContent);
19816
20233
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
@@ -19924,7 +20341,7 @@ function evaluateInitiativeCompletion(dir, ini) {
19924
20341
  // ../cli/src/core/resource-write.ts
19925
20342
  import fs6 from "fs";
19926
20343
  import path5 from "path";
19927
- import matter14 from "gray-matter";
20344
+ import matter15 from "gray-matter";
19928
20345
  var RESOURCES_DIR = "knowledge/tech/resources";
19929
20346
  var ResourceWriteError = class extends Error {
19930
20347
  constructor(code, message) {
@@ -20004,7 +20421,7 @@ function buildFrontmatter(id, input, base = {}) {
20004
20421
  return data;
20005
20422
  }
20006
20423
  function serialize(data, body) {
20007
- return matter14.stringify(`
20424
+ return matter15.stringify(`
20008
20425
  ${body.trim()}
20009
20426
  `, data);
20010
20427
  }
@@ -20044,7 +20461,7 @@ function updateResource(dir, id, input) {
20044
20461
  throw new ResourceWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
20045
20462
  }
20046
20463
  const raw = readFile(match.filePath);
20047
- const parsed = matter14(raw);
20464
+ const parsed = matter15(raw);
20048
20465
  const base = parsed.data ?? {};
20049
20466
  const data = buildFrontmatter(match.id || id, input, base);
20050
20467
  let body = parsed.content.trim();
@@ -20082,6 +20499,17 @@ function deleteResource(dir, id, opts = {}) {
20082
20499
  // src/tools.ts
20083
20500
  var ok = (data) => ({ ok: true, data });
20084
20501
  var fail = (message) => ({ ok: false, message });
20502
+ function preparePocReportTool(root) {
20503
+ const context = buildPocReportContext2(root);
20504
+ return context.eligible ? ok(context) : fail(context.handoff);
20505
+ }
20506
+ function persistPocReportTool(root, proposal, confirm2, force) {
20507
+ try {
20508
+ return ok(persistPocReport(root, proposal, { confirm: confirm2, force }));
20509
+ } catch (err) {
20510
+ return fail(err instanceof Error ? err.message : String(err));
20511
+ }
20512
+ }
20085
20513
  function projectStatus(root) {
20086
20514
  const explain = readJson(root, ".kaddo/explain.json");
20087
20515
  const hints = readJson(root, ".kaddo/graph-hints.json");
@@ -20174,12 +20602,12 @@ function markWorkItemReady(root, id, confirm2) {
20174
20602
  }
20175
20603
  const raw = readText(root, item.path);
20176
20604
  if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
20177
- const parsed = matter15(raw);
20605
+ const parsed = matter16(raw);
20178
20606
  const data = { ...parsed.data };
20179
20607
  const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
20180
20608
  data.status = "ready";
20181
20609
  data.ready_at = today3;
20182
- const newContent = matter15.stringify(parsed.content, data);
20610
+ const newContent = matter16.stringify(parsed.content, data);
20183
20611
  const posixPath = item.path.replace(/\\/g, "/");
20184
20612
  let newRelPath = item.path;
20185
20613
  if (posixPath.includes("/work-items/draft/")) {
@@ -20241,12 +20669,12 @@ function listGraphHints(root, filter = {}) {
20241
20669
  });
20242
20670
  }
20243
20671
  function importWorkItemTool(root, args) {
20244
- const source = args.source === "cli" || args.source === "admin" ? args.source : "chat";
20672
+ const source3 = args.source === "cli" || args.source === "admin" ? args.source : "chat";
20245
20673
  const onConflict = args.onConflict === "replace" || args.onConflict === "new-id" ? args.onConflict : void 0;
20246
20674
  try {
20247
20675
  const result = importWorkItem(root, {
20248
20676
  content: args.content,
20249
- source,
20677
+ source: source3,
20250
20678
  type: args.type,
20251
20679
  onConflict
20252
20680
  });
@@ -20407,7 +20835,7 @@ function deleteResourceTool(root, id, confirm2) {
20407
20835
  // src/multirepo.ts
20408
20836
  import fs7 from "fs";
20409
20837
  import path6 from "path";
20410
- import matter16 from "gray-matter";
20838
+ import matter17 from "gray-matter";
20411
20839
  import { parse as parseYaml11, stringify as stringifyYaml8 } from "yaml";
20412
20840
  var ok2 = (data) => ({ ok: true, data });
20413
20841
  var fail2 = (message) => ({ ok: false, message });
@@ -20559,7 +20987,7 @@ function validateWorkItemModulesTool(root, args) {
20559
20987
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
20560
20988
  const raw = readText(root, wi.path);
20561
20989
  if (!raw) return fail2(`Could not read Work Item file.`);
20562
- const parsed = matter16(raw);
20990
+ const parsed = matter17(raw);
20563
20991
  const data = parsed.data;
20564
20992
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
20565
20993
  const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
@@ -20612,7 +21040,7 @@ function getWorkItemContextTool(root, args) {
20612
21040
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
20613
21041
  const raw = readText(root, wi.path);
20614
21042
  if (!raw) return fail2(`Could not read Work Item file.`);
20615
- const parsed = matter16(raw);
21043
+ const parsed = matter17(raw);
20616
21044
  const data = parsed.data;
20617
21045
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
20618
21046
  const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
@@ -20698,7 +21126,7 @@ function suggestBranchStrategyTool(root, args) {
20698
21126
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
20699
21127
  const raw = readText(root, wi.path);
20700
21128
  if (!raw) return fail2(`Could not read Work Item file.`);
20701
- const parsed = matter16(raw);
21129
+ const parsed = matter17(raw);
20702
21130
  const data = parsed.data;
20703
21131
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
20704
21132
  const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
@@ -21307,10 +21735,10 @@ function getSystemNeighbors(dir, nodeId, opts = {}) {
21307
21735
  }
21308
21736
  return { seed: nodeId, nodes, relationships: rels, truncated };
21309
21737
  }
21310
- function findSystemPaths(dir, source, target, opts = {}) {
21738
+ function findSystemPaths(dir, source3, target, opts = {}) {
21311
21739
  const projection = getSystemMapProjection2(dir);
21312
21740
  const byId = new Map(projection.nodes.map((n) => [n.id, n]));
21313
- if (!byId.has(source) || !byId.has(target)) return [];
21741
+ if (!byId.has(source3) || !byId.has(target)) return [];
21314
21742
  const maxDepth = Math.max(1, opts.maxDepth ?? 4);
21315
21743
  const out = /* @__PURE__ */ new Map();
21316
21744
  for (const r2 of projection.relationships) {
@@ -21330,7 +21758,7 @@ function findSystemPaths(dir, source, target, opts = {}) {
21330
21758
  walk(nxt, [...path9, nxt]);
21331
21759
  }
21332
21760
  };
21333
- walk(source, [source]);
21761
+ walk(source3, [source3]);
21334
21762
  return paths;
21335
21763
  }
21336
21764
  function getImpactCandidates(dir, seedIds, opts = {}) {
@@ -21458,14 +21886,14 @@ function systemImpactCandidatesTool(root, args) {
21458
21886
  }
21459
21887
 
21460
21888
  // ../cli/src/services/integrations.ts
21461
- import matter18 from "gray-matter";
21889
+ import matter19 from "gray-matter";
21462
21890
  import { parse as parseYaml13, stringify as stringifyYaml10 } from "yaml";
21463
21891
 
21464
21892
  // ../cli/src/core/work-item-write.ts
21465
21893
  import fs9 from "fs";
21466
21894
  import path8 from "path";
21467
21895
  import crypto4 from "crypto";
21468
- import matter17 from "gray-matter";
21896
+ import matter18 from "gray-matter";
21469
21897
  var SECTION_ORDER2 = [
21470
21898
  "Actor",
21471
21899
  "Outcome",
@@ -22513,7 +22941,7 @@ async function integrationsWorkItemTool(root, args) {
22513
22941
  }
22514
22942
 
22515
22943
  // ../cli/src/core/context-pack.ts
22516
- import matter19 from "gray-matter";
22944
+ import matter20 from "gray-matter";
22517
22945
  var CONTEXT_PACK_VERSION = "1";
22518
22946
  var ARCH_DIR4 = "knowledge";
22519
22947
  function readScanJson(dir) {
@@ -22526,7 +22954,7 @@ function readScanJson(dir) {
22526
22954
  }
22527
22955
  }
22528
22956
  function firstParagraph4(markdown) {
22529
- const body = matter19(markdown).content.trim();
22957
+ const body = matter20(markdown).content.trim();
22530
22958
  const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
22531
22959
  return para ?? "";
22532
22960
  }
@@ -23939,6 +24367,24 @@ function createServer(root) {
23939
24367
  },
23940
24368
  async (args) => toolText(guarded(root, () => markWorkItemReady(root, args.id, args.confirm)))
23941
24369
  );
24370
+ server.registerTool(
24371
+ "kaddo_prepare_poc_report",
24372
+ {
24373
+ title: "Prepare final POC report context",
24374
+ description: "Build a deterministic, source-attributed POC report context. Does not call an LLM or write a report.",
24375
+ inputSchema: {}
24376
+ },
24377
+ async () => toolText(guarded(root, () => preparePocReportTool(root)))
24378
+ );
24379
+ server.registerTool(
24380
+ "kaddo_persist_poc_report",
24381
+ {
24382
+ title: "Persist confirmed final POC report",
24383
+ description: "Preview a proposed final POC report by default. With confirm=true, persist the next immutable version. Never provide credential values.",
24384
+ inputSchema: { proposal: z4.string(), confirm: z4.boolean().optional(), force: z4.boolean().optional() }
24385
+ },
24386
+ async (args) => toolText(guarded(root, () => persistPocReportTool(root, args.proposal, args.confirm, args.force)))
24387
+ );
23942
24388
  server.registerTool(
23943
24389
  "kaddo_list_resources",
23944
24390
  { title: "List Project Resources", description: "List external systems the project uses (databases, cloud, APIs\u2026), read-only, no secrets.", inputSchema: {} },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.117.0",
3
+ "version": "3.119.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": {