@kaddo/mcp 3.128.0 → 3.129.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +959 -68
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -169,6 +169,8 @@ var DERIVED_WRITE_FILES = /* @__PURE__ */ new Set([
169
169
  ".kaddo/graph.mmd",
170
170
  ".kaddo/graph-hints.md",
171
171
  ".kaddo/graph-hints.json",
172
+ ".kaddo/consolidation-plan.md",
173
+ ".kaddo/consolidation-plan.json",
172
174
  ".kaddo/modules.yml"
173
175
  ]);
174
176
  var DERIVED_EXPORTS_RE = /^\.kaddo\/exports\/[^/]+\.capsule\.(md|json)$/;
@@ -507,6 +509,21 @@ ${issues}`);
507
509
  }
508
510
  return parsed.data;
509
511
  }
512
+ function requireConfig(dir) {
513
+ let config;
514
+ try {
515
+ config = loadConfig(dir);
516
+ } catch (err) {
517
+ const message = err instanceof ConfigError ? err.message : String(err);
518
+ console.error(message);
519
+ process.exit(1);
520
+ }
521
+ if (!config) {
522
+ console.error("No .kaddo/config.yml found. Run `kaddo init` first.");
523
+ process.exit(1);
524
+ }
525
+ return config;
526
+ }
510
527
  function projectRole(config) {
511
528
  return config.project.role ?? config.multirepo?.role;
512
529
  }
@@ -4547,7 +4564,8 @@ var AGENT_GROUPS = {
4547
4564
  "work-item-agent.md",
4548
4565
  "implementation-agent.md",
4549
4566
  "ownership-agent.md",
4550
- "git-strategy-agent.md"
4567
+ "git-strategy-agent.md",
4568
+ "knowledge-consolidation-agent.md"
4551
4569
  ],
4552
4570
  utilities: ["legacy-agent.md"]
4553
4571
  };
@@ -5226,7 +5244,7 @@ var KNOWLEDGE_FILES = [
5226
5244
  function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
5227
5245
  const config = loadConfig(dir);
5228
5246
  const rec = resolveNextStep(dir, now);
5229
- const stub = (overall2, label, command) => ({
5247
+ const stub = (overall2, label2, command) => ({
5230
5248
  project_name: config?.project.name ?? "unknown",
5231
5249
  project_type: config?.project.state ?? "unknown",
5232
5250
  overall: overall2,
@@ -5249,7 +5267,7 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
5249
5267
  resolved_questions: 0,
5250
5268
  deferred_questions: 0
5251
5269
  },
5252
- recommended_next_step: { label, ...command ? { command } : {} },
5270
+ recommended_next_step: { label: label2, ...command ? { command } : {} },
5253
5271
  nextStepRecommendation: rec
5254
5272
  });
5255
5273
  if (!config) return stub("not-initialized", rec.label, rec.command);
@@ -6124,6 +6142,69 @@ After a POC has a canonical conclusion and Core has prepared its deterministic r
6124
6142
  15. Traceability and Sources
6125
6143
  `
6126
6144
  );
6145
+ var KNOWLEDGE_CONSOLIDATION = skill(
6146
+ "knowledge-consolidation",
6147
+ "Knowledge Consolidation Skill",
6148
+ "delivery",
6149
+ ["knowledge-consolidation-agent", "work-item-agent", "architecture-agent"],
6150
+ `
6151
+ # Knowledge Consolidation Skill
6152
+
6153
+ ## Purpose
6154
+
6155
+ Standardize the SEMANTIC review of a deterministic Consolidation Plan so canonical knowledge becomes
6156
+ more precise, connected, consistent and retrievable \u2014 without losing history, decisions or evidence.
6157
+
6158
+ ## When to use
6159
+
6160
+ After \`kaddo knowledge consolidate\` (or the \`kaddo_generate_consolidation_plan\` tool) has produced
6161
+ \`.kaddo/consolidation-plan.{md,json}\`, and a human or agent wants to review the opportunities and
6162
+ prepare refinements. Never as an autonomous rewrite of \`knowledge/\`.
6163
+
6164
+ ## Inputs
6165
+
6166
+ The Consolidation Plan (\`.kaddo/consolidation-plan.json\`), the referenced source artifacts, and the
6167
+ context pack. Each opportunity carries: category, classification, confidence, source/target, evidence,
6168
+ rationale, suggested action, and \`requires_semantic_review\`.
6169
+
6170
+ ## Output
6171
+
6172
+ For each opportunity the agent chooses to act on: a reviewable proposal that states what would change,
6173
+ what content is added/moved/summarized, what must be preserved, and which claims still need
6174
+ confirmation \u2014 with a diff or equivalent. No canonical file is edited by this skill; application goes
6175
+ through the existing controlled-edit flows after human approval.
6176
+
6177
+ ## Rules
6178
+
6179
+ - Distinguish \`observed\` (verifiable fact) from \`suggested\` (a candidate needing judgement). Never
6180
+ present a \`suggested\` candidate as a verified conclusion.
6181
+ - Classify every claim as Observed / Derived / Suggested / Unknown.
6182
+ - Do not merge Business/Product/Tech just because they share a topic \u2014 preserve layer responsibilities.
6183
+ - Do not move every historical learning into current knowledge; incorporate only live rules, WITH a
6184
+ reference back to the originating Work Item. Never summarize or delete the Work Item.
6185
+ - Do not invent relationships, decisions or business rules; a missing optional relationship is an
6186
+ opportunity, not an error.
6187
+ - For a potential inconsistency, present BOTH sources and say whether the conflict is verified or
6188
+ potential. Never pick a winner arbitrarily.
6189
+ - Preserve critical constraints, decisions and reasons, exceptions, known risks, technical contracts,
6190
+ implementation evidence, provenance and necessary history.
6191
+ - Never reveal secret values or resolve secret references; never connect to a Project Resource.
6192
+ - Do not commit, push or release automatically.
6193
+
6194
+ ## Quality checklist
6195
+
6196
+ - Every proposal cites its source artifact(s) and evidence.
6197
+ - Facts are separated from interpretations.
6198
+ - Preserved information is named explicitly.
6199
+ - Claims needing confirmation are flagged.
6200
+ - No canonical artifact was modified by the analysis or the review.
6201
+
6202
+ ## Example output
6203
+
6204
+ A short set of proposals, each with evidence, a before/after (or diff) and an explicit list of what
6205
+ must be preserved, ready for human approval.
6206
+ `
6207
+ );
6127
6208
  var SKILLS = [
6128
6209
  ADR_WRITING,
6129
6210
  WORK_ITEM_REFINEMENT,
@@ -6135,10 +6216,11 @@ var SKILLS = [
6135
6216
  MODULE_CONTEXT_REFINEMENT,
6136
6217
  EVIDENCE_VERIFICATION,
6137
6218
  LEGACY_RISK_ASSESSMENT,
6138
- POC_REPORT_WRITING
6219
+ POC_REPORT_WRITING,
6220
+ KNOWLEDGE_CONSOLIDATION
6139
6221
  ];
6140
6222
  var SKILL_GROUPS = {
6141
- delivery: ["work-item-refinement", "implementation-planning", "learning-capture", "evidence-verification", "legacy-risk-assessment", "poc-report-writing"],
6223
+ delivery: ["work-item-refinement", "implementation-planning", "learning-capture", "evidence-verification", "legacy-risk-assessment", "poc-report-writing", "knowledge-consolidation"],
6142
6224
  tech: ["adr-writing", "ownership-suggestion", "graph-metadata-review", "module-context-refinement"],
6143
6225
  integration: ["capsule-writing"]
6144
6226
  };
@@ -6334,6 +6416,24 @@ var RESPONSIBILITY_MATRIX = {
6334
6416
  ],
6335
6417
  next: ["kaddo graph export"]
6336
6418
  },
6419
+ "knowledge-consolidation-agent": {
6420
+ agent: "knowledge-consolidation-agent",
6421
+ responsibleFor: [
6422
+ "Semantic review of the deterministic Consolidation Plan",
6423
+ "Preparing reviewable knowledge refinements"
6424
+ ],
6425
+ produces: ["proposed refinements (before/after) for human approval"],
6426
+ canSuggest: ["kaddo knowledge consolidate"],
6427
+ cannotSuggest: [
6428
+ "code",
6429
+ "git",
6430
+ "commits",
6431
+ "modifying canonical knowledge without confirmation",
6432
+ "inventing facts or relationships",
6433
+ "deleting history"
6434
+ ],
6435
+ next: ["kaddo knowledge consolidate"]
6436
+ },
6337
6437
  "ownership-agent": {
6338
6438
  agent: "ownership-agent",
6339
6439
  responsibleFor: ["Precise code: ownership for Work Items and artifacts"],
@@ -8494,6 +8594,78 @@ ADRs) and re-runs \`kaddo graph export\`.
8494
8594
  - No files were modified; no Git was run.
8495
8595
  - The human is asked to confirm and re-export the graph.
8496
8596
  `;
8597
+ var KNOWLEDGE_CONSOLIDATION_AGENT = `# Knowledge Consolidation Agent
8598
+
8599
+ ## Role
8600
+
8601
+ You are the Kaddo Knowledge Consolidation Agent. Your job is to perform the **semantic review** of
8602
+ the deterministic **Consolidation Plan** produced by \`kaddo knowledge consolidate\`, and to prepare
8603
+ **reviewable refinements** that make canonical knowledge more precise, connected, consistent and
8604
+ retrievable \u2014 without losing history, decisions or evidence.
8605
+
8606
+ You do not write code, you do not modify canonical files, and you never run Git. You propose; a human
8607
+ confirms, and application goes through Kaddo's existing controlled-edit flows.
8608
+
8609
+ ## When to Use
8610
+
8611
+ Use this agent after \`kaddo knowledge consolidate\` (or the \`kaddo_generate_consolidation_plan\`
8612
+ tool) has produced \`.kaddo/consolidation-plan.md\` and \`.kaddo/consolidation-plan.json\`, and the
8613
+ plan contains opportunities flagged \`requires_semantic_review\`.
8614
+
8615
+ ## Input Required
8616
+
8617
+ Provide \`.kaddo/context-pack.md\` and \`.kaddo/consolidation-plan.json\` as the primary inputs, plus
8618
+ the source artifacts each opportunity references under \`knowledge/\` (and completed Work Items under
8619
+ \`knowledge/delivery/work-items/completed/\` for learning opportunities).
8620
+
8621
+ ## Expected Output
8622
+
8623
+ For each opportunity you choose to act on, a proposal that states what would change, what content is
8624
+ added / moved / summarized, what must be preserved, and which claims still need confirmation \u2014 with a
8625
+ diff or equivalent before/after.
8626
+
8627
+ ## Instructions
8628
+
8629
+ 1. Read the plan and work one opportunity at a time, highest-signal first.
8630
+ 2. Separate what you **observe** in the files from what you **suggest** \u2014 never present a suggested
8631
+ candidate as a verified conclusion.
8632
+ 3. For possible redundancy, compare the actual sections; propose a single reference location only if
8633
+ one genuinely restates the other. Preserve layer separation (Business / Product / Tech / Delivery).
8634
+ 4. For an unincorporated learning, decide whether it is a live rule; if so, propose incorporating it
8635
+ into the right Tech artifact **with a reference back to the originating Work Item**. Never summarize
8636
+ or delete the Work Item.
8637
+ 5. For a relationship opportunity, propose the relationship only when existing knowledge justifies it;
8638
+ a missing optional relationship is an opportunity, not an error.
8639
+ 6. For a potential inconsistency, present **both** sources and say whether the conflict is verified or
8640
+ potential. Do not pick a winner arbitrarily.
8641
+ 7. Classify every claim as Observed / Derived / Suggested / Unknown, and mark what needs confirmation.
8642
+
8643
+ ## Constraints
8644
+
8645
+ - Do **not** modify canonical files \u2014 propose changes for a human to apply.
8646
+ - Do **not** invent facts, relationships, decisions or business rules.
8647
+ - Do **not** delete historical information, change Work Item states, or alter verification results.
8648
+ - Do **not** reveal secret values or resolve secret references; do **not** connect to a Project Resource.
8649
+ - Do **not** run Git, commit, push or release.
8650
+
8651
+ ## Output Format
8652
+
8653
+ Per opportunity: the opportunity id, classification and confidence, the proposed before/after (or
8654
+ diff), the explicit list of what must be preserved, and the open questions that need confirmation.
8655
+
8656
+ ## Where to Save the Result
8657
+
8658
+ Nothing is saved automatically. The human reviews the proposals and applies the approved ones through
8659
+ Kaddo's existing controlled-edit flows. Re-run \`kaddo knowledge consolidate\` afterwards to confirm.
8660
+
8661
+ ## Quality Checklist
8662
+
8663
+ - Every proposal cites its source artifact(s) and evidence from the plan.
8664
+ - Facts are separated from interpretations; suggested candidates are never asserted as fact.
8665
+ - Preserved information (constraints, decisions, exceptions, risks, contracts, provenance) is named.
8666
+ - No canonical artifact was modified; no Git was run.
8667
+ - The human is asked to confirm before anything is applied.
8668
+ `;
8497
8669
  var AGENT_PROMPTS = [
8498
8670
  { fileName: "capability-agent.md", content: CAPABILITY_AGENT },
8499
8671
  { fileName: "architecture-agent.md", content: ARCHITECTURE_AGENT },
@@ -8520,7 +8692,9 @@ var AGENT_PROMPTS = [
8520
8692
  // Knowledge Capsule refinement (external context — VS-054)
8521
8693
  { fileName: "capsule-agent.md", content: CAPSULE_AGENT },
8522
8694
  // Graph relationship quality (metadata hints → precise front matter — VS-056)
8523
- { fileName: "graph-agent.md", content: GRAPH_AGENT }
8695
+ { fileName: "graph-agent.md", content: GRAPH_AGENT },
8696
+ // Knowledge Consolidation semantic review (deterministic plan → reviewable refinements — WI-070)
8697
+ { fileName: "knowledge-consolidation-agent.md", content: KNOWLEDGE_CONSOLIDATION_AGENT }
8524
8698
  // Every official prompt ends with its responsibility boundaries + Agent Trace footer.
8525
8699
  ].map((p) => ({ fileName: p.fileName, content: withResponsibilityTrace(p.fileName, p.content) }));
8526
8700
 
@@ -8727,9 +8901,9 @@ function installedAssetsSummary(dir) {
8727
8901
  // ../cli/src/core/roadmap-quality.ts
8728
8902
  var ROADMAP_PATH = "knowledge/delivery/roadmap.md";
8729
8903
  var INITIATIVE_RE2 = /^#{2,4}\s+(RM-[\w.-]+)\s*[:\-–]?\s*(.*)$/;
8730
- function fieldPresent(lines, label) {
8904
+ function fieldPresent(lines, label2) {
8731
8905
  for (let i = 0; i < lines.length; i++) {
8732
- const m2 = lines[i].match(new RegExp(`^\\s*[-*]?\\s*(?:\\*\\*)?\\s*(${label.source})\\s*(?:\\*\\*)?\\s*:\\s*(.*)$`, "i"));
8906
+ const m2 = lines[i].match(new RegExp(`^\\s*[-*]?\\s*(?:\\*\\*)?\\s*(${label2.source})\\s*(?:\\*\\*)?\\s*:\\s*(.*)$`, "i"));
8733
8907
  if (!m2) continue;
8734
8908
  const inline = m2[2].trim();
8735
8909
  if (inline) return true;
@@ -8831,13 +9005,13 @@ function renderSignalsCompact(signals) {
8831
9005
  ["external_integrations", "External integrations"],
8832
9006
  ["environment", "Environment"]
8833
9007
  ];
8834
- for (const [key, label] of CATEGORIES) {
9008
+ for (const [key, label2] of CATEGORIES) {
8835
9009
  const arr = signals[key];
8836
9010
  if (arr.length === 0) continue;
8837
9011
  if (arr.length === 1) {
8838
- lines.push(`- ${label}: ${arr[0].label}`);
9012
+ lines.push(`- ${label2}: ${arr[0].label}`);
8839
9013
  } else {
8840
- lines.push(`- ${label}: ${arr.length}`);
9014
+ lines.push(`- ${label2}: ${arr.length}`);
8841
9015
  }
8842
9016
  }
8843
9017
  return lines.length > 0 ? lines.join("\n") + "\n" : "No signals detected.\n";
@@ -11035,9 +11209,9 @@ function renderImpactMarkdown(r2, opts = {}) {
11035
11209
  ["validation section", gp.missing_validation]
11036
11210
  ];
11037
11211
  L.push("## Historical Documentation Completeness", "");
11038
- for (const [label, items] of historical) {
11212
+ for (const [label2, items] of historical) {
11039
11213
  const completed = items.filter((item) => item.status === "completed");
11040
- if (completed.length > 0) L.push(`- Completed Work Items without detected ${label}: ${completed.length}`);
11214
+ if (completed.length > 0) L.push(`- Completed Work Items without detected ${label2}: ${completed.length}`);
11041
11215
  }
11042
11216
  if (!historical.some(([, items]) => items.some((item) => item.status === "completed"))) {
11043
11217
  L.push("No historical documentation completeness gaps were detected.");
@@ -11812,6 +11986,27 @@ var RESOURCES = [
11812
11986
  return parts;
11813
11987
  }
11814
11988
  },
11989
+ {
11990
+ uri: "kaddo://consolidation-plan",
11991
+ name: "Kaddo consolidation plan",
11992
+ description: "Knowledge Consolidation Plan (.kaddo/consolidation-plan.md + .json) \u2014 reviewable opportunities to optimize, connect and refine canonical knowledge. Read-only.",
11993
+ mimeType: "text/markdown",
11994
+ read: (root) => {
11995
+ const md = readText(root, ".kaddo/consolidation-plan.md");
11996
+ const json = readText(root, ".kaddo/consolidation-plan.json");
11997
+ if (!md && !json) {
11998
+ return text2(
11999
+ "kaddo://consolidation-plan",
12000
+ "Consolidation Plan not found. Run `kaddo knowledge consolidate` (or the kaddo_generate_consolidation_plan tool) first.",
12001
+ "text/plain"
12002
+ );
12003
+ }
12004
+ const parts = [];
12005
+ if (md) parts.push({ uri: "kaddo://consolidation-plan", text: md, mimeType: "text/markdown" });
12006
+ if (json) parts.push({ uri: "kaddo://consolidation-plan.json", text: json, mimeType: "application/json" });
12007
+ return parts;
12008
+ }
12009
+ },
11815
12010
  {
11816
12011
  uri: "kaddo://build-contract",
11817
12012
  name: "Kaddo build contract",
@@ -12168,7 +12363,7 @@ function getPrompt(root, name) {
12168
12363
  }
12169
12364
 
12170
12365
  // src/tools.ts
12171
- import matter24 from "gray-matter";
12366
+ import matter25 from "gray-matter";
12172
12367
 
12173
12368
  // ../cli/dist/core.js
12174
12369
  import fs3 from "fs";
@@ -12198,22 +12393,23 @@ import matter102 from "gray-matter";
12198
12393
  import { parse as parseYaml82 } from "yaml";
12199
12394
  import matter112 from "gray-matter";
12200
12395
  import matter122 from "gray-matter";
12396
+ import matter132 from "gray-matter";
12201
12397
  import { parse as parseYaml92, stringify as stringifyYaml52 } from "yaml";
12202
12398
  import fs4 from "fs";
12203
12399
  import path4 from "path";
12204
12400
  import crypto2 from "crypto";
12205
- import matter132 from "gray-matter";
12401
+ import matter142 from "gray-matter";
12206
12402
  import crypto3 from "crypto";
12207
12403
  import fs5 from "fs";
12208
- import matter142 from "gray-matter";
12209
12404
  import matter15 from "gray-matter";
12405
+ import matter16 from "gray-matter";
12210
12406
  import { parse as parseYaml102, stringify as stringifyYaml6 } from "yaml";
12211
12407
  import { parse as parseYaml11, stringify as stringifyYaml7 } from "yaml";
12212
- import matter16 from "gray-matter";
12213
12408
  import matter17 from "gray-matter";
12214
12409
  import matter18 from "gray-matter";
12215
12410
  import matter19 from "gray-matter";
12216
12411
  import matter20 from "gray-matter";
12412
+ import matter21 from "gray-matter";
12217
12413
  function exists2(p) {
12218
12414
  return fs3.existsSync(p);
12219
12415
  }
@@ -14688,7 +14884,8 @@ var AGENT_GROUPS2 = {
14688
14884
  "work-item-agent.md",
14689
14885
  "implementation-agent.md",
14690
14886
  "ownership-agent.md",
14691
- "git-strategy-agent.md"
14887
+ "git-strategy-agent.md",
14888
+ "knowledge-consolidation-agent.md"
14692
14889
  ],
14693
14890
  utilities: ["legacy-agent.md"]
14694
14891
  };
@@ -15718,6 +15915,69 @@ After a POC has a canonical conclusion and Core has prepared its deterministic r
15718
15915
  15. Traceability and Sources
15719
15916
  `
15720
15917
  );
15918
+ var KNOWLEDGE_CONSOLIDATION2 = skill2(
15919
+ "knowledge-consolidation",
15920
+ "Knowledge Consolidation Skill",
15921
+ "delivery",
15922
+ ["knowledge-consolidation-agent", "work-item-agent", "architecture-agent"],
15923
+ `
15924
+ # Knowledge Consolidation Skill
15925
+
15926
+ ## Purpose
15927
+
15928
+ Standardize the SEMANTIC review of a deterministic Consolidation Plan so canonical knowledge becomes
15929
+ more precise, connected, consistent and retrievable \u2014 without losing history, decisions or evidence.
15930
+
15931
+ ## When to use
15932
+
15933
+ After \`kaddo knowledge consolidate\` (or the \`kaddo_generate_consolidation_plan\` tool) has produced
15934
+ \`.kaddo/consolidation-plan.{md,json}\`, and a human or agent wants to review the opportunities and
15935
+ prepare refinements. Never as an autonomous rewrite of \`knowledge/\`.
15936
+
15937
+ ## Inputs
15938
+
15939
+ The Consolidation Plan (\`.kaddo/consolidation-plan.json\`), the referenced source artifacts, and the
15940
+ context pack. Each opportunity carries: category, classification, confidence, source/target, evidence,
15941
+ rationale, suggested action, and \`requires_semantic_review\`.
15942
+
15943
+ ## Output
15944
+
15945
+ For each opportunity the agent chooses to act on: a reviewable proposal that states what would change,
15946
+ what content is added/moved/summarized, what must be preserved, and which claims still need
15947
+ confirmation \u2014 with a diff or equivalent. No canonical file is edited by this skill; application goes
15948
+ through the existing controlled-edit flows after human approval.
15949
+
15950
+ ## Rules
15951
+
15952
+ - Distinguish \`observed\` (verifiable fact) from \`suggested\` (a candidate needing judgement). Never
15953
+ present a \`suggested\` candidate as a verified conclusion.
15954
+ - Classify every claim as Observed / Derived / Suggested / Unknown.
15955
+ - Do not merge Business/Product/Tech just because they share a topic \u2014 preserve layer responsibilities.
15956
+ - Do not move every historical learning into current knowledge; incorporate only live rules, WITH a
15957
+ reference back to the originating Work Item. Never summarize or delete the Work Item.
15958
+ - Do not invent relationships, decisions or business rules; a missing optional relationship is an
15959
+ opportunity, not an error.
15960
+ - For a potential inconsistency, present BOTH sources and say whether the conflict is verified or
15961
+ potential. Never pick a winner arbitrarily.
15962
+ - Preserve critical constraints, decisions and reasons, exceptions, known risks, technical contracts,
15963
+ implementation evidence, provenance and necessary history.
15964
+ - Never reveal secret values or resolve secret references; never connect to a Project Resource.
15965
+ - Do not commit, push or release automatically.
15966
+
15967
+ ## Quality checklist
15968
+
15969
+ - Every proposal cites its source artifact(s) and evidence.
15970
+ - Facts are separated from interpretations.
15971
+ - Preserved information is named explicitly.
15972
+ - Claims needing confirmation are flagged.
15973
+ - No canonical artifact was modified by the analysis or the review.
15974
+
15975
+ ## Example output
15976
+
15977
+ A short set of proposals, each with evidence, a before/after (or diff) and an explicit list of what
15978
+ must be preserved, ready for human approval.
15979
+ `
15980
+ );
15721
15981
  var SKILLS2 = [
15722
15982
  ADR_WRITING2,
15723
15983
  WORK_ITEM_REFINEMENT2,
@@ -15729,10 +15989,11 @@ var SKILLS2 = [
15729
15989
  MODULE_CONTEXT_REFINEMENT2,
15730
15990
  EVIDENCE_VERIFICATION2,
15731
15991
  LEGACY_RISK_ASSESSMENT2,
15732
- POC_REPORT_WRITING2
15992
+ POC_REPORT_WRITING2,
15993
+ KNOWLEDGE_CONSOLIDATION2
15733
15994
  ];
15734
15995
  var SKILL_GROUPS2 = {
15735
- delivery: ["work-item-refinement", "implementation-planning", "learning-capture", "evidence-verification", "legacy-risk-assessment", "poc-report-writing"],
15996
+ delivery: ["work-item-refinement", "implementation-planning", "learning-capture", "evidence-verification", "legacy-risk-assessment", "poc-report-writing", "knowledge-consolidation"],
15736
15997
  tech: ["adr-writing", "ownership-suggestion", "graph-metadata-review", "module-context-refinement"],
15737
15998
  integration: ["capsule-writing"]
15738
15999
  };
@@ -15923,6 +16184,24 @@ var RESPONSIBILITY_MATRIX2 = {
15923
16184
  ],
15924
16185
  next: ["kaddo graph export"]
15925
16186
  },
16187
+ "knowledge-consolidation-agent": {
16188
+ agent: "knowledge-consolidation-agent",
16189
+ responsibleFor: [
16190
+ "Semantic review of the deterministic Consolidation Plan",
16191
+ "Preparing reviewable knowledge refinements"
16192
+ ],
16193
+ produces: ["proposed refinements (before/after) for human approval"],
16194
+ canSuggest: ["kaddo knowledge consolidate"],
16195
+ cannotSuggest: [
16196
+ "code",
16197
+ "git",
16198
+ "commits",
16199
+ "modifying canonical knowledge without confirmation",
16200
+ "inventing facts or relationships",
16201
+ "deleting history"
16202
+ ],
16203
+ next: ["kaddo knowledge consolidate"]
16204
+ },
15926
16205
  "ownership-agent": {
15927
16206
  agent: "ownership-agent",
15928
16207
  responsibleFor: ["Precise code: ownership for Work Items and artifacts"],
@@ -18081,6 +18360,78 @@ ADRs) and re-runs \`kaddo graph export\`.
18081
18360
  - No files were modified; no Git was run.
18082
18361
  - The human is asked to confirm and re-export the graph.
18083
18362
  `;
18363
+ var KNOWLEDGE_CONSOLIDATION_AGENT2 = `# Knowledge Consolidation Agent
18364
+
18365
+ ## Role
18366
+
18367
+ You are the Kaddo Knowledge Consolidation Agent. Your job is to perform the **semantic review** of
18368
+ the deterministic **Consolidation Plan** produced by \`kaddo knowledge consolidate\`, and to prepare
18369
+ **reviewable refinements** that make canonical knowledge more precise, connected, consistent and
18370
+ retrievable \u2014 without losing history, decisions or evidence.
18371
+
18372
+ You do not write code, you do not modify canonical files, and you never run Git. You propose; a human
18373
+ confirms, and application goes through Kaddo's existing controlled-edit flows.
18374
+
18375
+ ## When to Use
18376
+
18377
+ Use this agent after \`kaddo knowledge consolidate\` (or the \`kaddo_generate_consolidation_plan\`
18378
+ tool) has produced \`.kaddo/consolidation-plan.md\` and \`.kaddo/consolidation-plan.json\`, and the
18379
+ plan contains opportunities flagged \`requires_semantic_review\`.
18380
+
18381
+ ## Input Required
18382
+
18383
+ Provide \`.kaddo/context-pack.md\` and \`.kaddo/consolidation-plan.json\` as the primary inputs, plus
18384
+ the source artifacts each opportunity references under \`knowledge/\` (and completed Work Items under
18385
+ \`knowledge/delivery/work-items/completed/\` for learning opportunities).
18386
+
18387
+ ## Expected Output
18388
+
18389
+ For each opportunity you choose to act on, a proposal that states what would change, what content is
18390
+ added / moved / summarized, what must be preserved, and which claims still need confirmation \u2014 with a
18391
+ diff or equivalent before/after.
18392
+
18393
+ ## Instructions
18394
+
18395
+ 1. Read the plan and work one opportunity at a time, highest-signal first.
18396
+ 2. Separate what you **observe** in the files from what you **suggest** \u2014 never present a suggested
18397
+ candidate as a verified conclusion.
18398
+ 3. For possible redundancy, compare the actual sections; propose a single reference location only if
18399
+ one genuinely restates the other. Preserve layer separation (Business / Product / Tech / Delivery).
18400
+ 4. For an unincorporated learning, decide whether it is a live rule; if so, propose incorporating it
18401
+ into the right Tech artifact **with a reference back to the originating Work Item**. Never summarize
18402
+ or delete the Work Item.
18403
+ 5. For a relationship opportunity, propose the relationship only when existing knowledge justifies it;
18404
+ a missing optional relationship is an opportunity, not an error.
18405
+ 6. For a potential inconsistency, present **both** sources and say whether the conflict is verified or
18406
+ potential. Do not pick a winner arbitrarily.
18407
+ 7. Classify every claim as Observed / Derived / Suggested / Unknown, and mark what needs confirmation.
18408
+
18409
+ ## Constraints
18410
+
18411
+ - Do **not** modify canonical files \u2014 propose changes for a human to apply.
18412
+ - Do **not** invent facts, relationships, decisions or business rules.
18413
+ - Do **not** delete historical information, change Work Item states, or alter verification results.
18414
+ - Do **not** reveal secret values or resolve secret references; do **not** connect to a Project Resource.
18415
+ - Do **not** run Git, commit, push or release.
18416
+
18417
+ ## Output Format
18418
+
18419
+ Per opportunity: the opportunity id, classification and confidence, the proposed before/after (or
18420
+ diff), the explicit list of what must be preserved, and the open questions that need confirmation.
18421
+
18422
+ ## Where to Save the Result
18423
+
18424
+ Nothing is saved automatically. The human reviews the proposals and applies the approved ones through
18425
+ Kaddo's existing controlled-edit flows. Re-run \`kaddo knowledge consolidate\` afterwards to confirm.
18426
+
18427
+ ## Quality Checklist
18428
+
18429
+ - Every proposal cites its source artifact(s) and evidence from the plan.
18430
+ - Facts are separated from interpretations; suggested candidates are never asserted as fact.
18431
+ - Preserved information (constraints, decisions, exceptions, risks, contracts, provenance) is named.
18432
+ - No canonical artifact was modified; no Git was run.
18433
+ - The human is asked to confirm before anything is applied.
18434
+ `;
18084
18435
  var AGENT_PROMPTS2 = [
18085
18436
  { fileName: "capability-agent.md", content: CAPABILITY_AGENT2 },
18086
18437
  { fileName: "architecture-agent.md", content: ARCHITECTURE_AGENT2 },
@@ -18107,7 +18458,9 @@ var AGENT_PROMPTS2 = [
18107
18458
  // Knowledge Capsule refinement (external context — VS-054)
18108
18459
  { fileName: "capsule-agent.md", content: CAPSULE_AGENT2 },
18109
18460
  // Graph relationship quality (metadata hints → precise front matter — VS-056)
18110
- { fileName: "graph-agent.md", content: GRAPH_AGENT2 }
18461
+ { fileName: "graph-agent.md", content: GRAPH_AGENT2 },
18462
+ // Knowledge Consolidation semantic review (deterministic plan → reviewable refinements — WI-070)
18463
+ { fileName: "knowledge-consolidation-agent.md", content: KNOWLEDGE_CONSOLIDATION_AGENT2 }
18111
18464
  // Every official prompt ends with its responsibility boundaries + Agent Trace footer.
18112
18465
  ].map((p) => ({ fileName: p.fileName, content: withResponsibilityTrace2(p.fileName, p.content) }));
18113
18466
  function withAgentFrontMatter2(fileName, content) {
@@ -18985,7 +19338,7 @@ function parseOriginalSnapshot(fm) {
18985
19338
  function readBody(filePath) {
18986
19339
  try {
18987
19340
  const raw = readFile2(filePath);
18988
- return matter122(raw).content;
19341
+ return matter132(raw).content;
18989
19342
  } catch {
18990
19343
  return "";
18991
19344
  }
@@ -19270,7 +19623,7 @@ function ensureH1(body, title) {
19270
19623
  ${trimmed}`;
19271
19624
  }
19272
19625
  function serialize2(data, body) {
19273
- return matter132.stringify(`
19626
+ return matter142.stringify(`
19274
19627
  ${body.trim()}
19275
19628
  `, data);
19276
19629
  }
@@ -20184,7 +20537,7 @@ function detectFormat(raw) {
20184
20537
  if (!trimmed) throw new ImportError("EMPTY_INPUT", "Import content is empty.");
20185
20538
  if (trimmed.startsWith("---")) {
20186
20539
  try {
20187
- const { data } = matter142(trimmed);
20540
+ const { data } = matter15(trimmed);
20188
20541
  if (data && typeof data === "object" && Object.keys(data).length > 0) {
20189
20542
  const d3 = data;
20190
20543
  if (d3.type === "work-item" || d3.id && typeof d3.id === "string" && /^WI-\d+/.test(d3.id)) {
@@ -20206,7 +20559,7 @@ function parseContent(raw) {
20206
20559
  let bodyText = trimmed;
20207
20560
  if (format === "kaddo-frontmatter" || format === "markdown-frontmatter") {
20208
20561
  try {
20209
- const parsed = matter142(trimmed);
20562
+ const parsed = matter15(trimmed);
20210
20563
  frontmatterFields = parsed.data ?? {};
20211
20564
  bodyText = parsed.content.trim();
20212
20565
  } catch {
@@ -20303,7 +20656,7 @@ function findDuplicateByHash(dir, hash) {
20303
20656
  const raw = readFile2(item.filePath);
20304
20657
  if (!raw) continue;
20305
20658
  try {
20306
- const { data } = matter142(raw);
20659
+ const { data } = matter15(raw);
20307
20660
  const source22 = data?.source;
20308
20661
  if (source22 && typeof source22 === "object" && !Array.isArray(source22)) {
20309
20662
  const s = source22;
@@ -21198,7 +21551,7 @@ function buildInitiativeContent(id, input) {
21198
21551
  if (input.priority) data.priority = input.priority;
21199
21552
  if (input.source) data.source = input.source;
21200
21553
  if (input.sourceId) data.source_id = input.sourceId;
21201
- return matter16.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
21554
+ return matter17.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
21202
21555
  }
21203
21556
  function createInitiative(dir, input) {
21204
21557
  if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
@@ -21222,10 +21575,10 @@ function transitionInitiative(dir, id, to) {
21222
21575
  throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
21223
21576
  }
21224
21577
  const raw = readFile2(ini.filePath);
21225
- const { data, content } = matter16(raw, {});
21578
+ const { data, content } = matter17(raw, {});
21226
21579
  data.status = to;
21227
21580
  if (to === "completed") data.completed_at = today2();
21228
- const newContent = matter16.stringify(content, data);
21581
+ const newContent = matter17.stringify(content, data);
21229
21582
  writeFile2(ini.filePath, newContent);
21230
21583
  const updated = parseInitiative2(dir, ini.filePath, newContent);
21231
21584
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
@@ -21235,7 +21588,7 @@ function addCandidate(dir, id, candidate) {
21235
21588
  const ini = getInitiative(dir, id);
21236
21589
  if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
21237
21590
  const raw = readFile2(ini.filePath);
21238
- const { data, content } = matter16(raw, {});
21591
+ const { data, content } = matter17(raw, {});
21239
21592
  const existing = Array.isArray(data.candidates) ? data.candidates : [];
21240
21593
  const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
21241
21594
  const normalized = normalizeCandidate(candidate, fallbackId);
@@ -21243,7 +21596,7 @@ function addCandidate(dir, id, candidate) {
21243
21596
  throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
21244
21597
  }
21245
21598
  data.candidates = [...existing, candidateToData(normalized)];
21246
- const newContent = matter16.stringify(content, data);
21599
+ const newContent = matter17.stringify(content, data);
21247
21600
  writeFile2(ini.filePath, newContent);
21248
21601
  const updated = parseInitiative2(dir, ini.filePath, newContent);
21249
21602
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
@@ -21253,7 +21606,7 @@ function updateInitiative(dir, id, patch) {
21253
21606
  const ini = getInitiative(dir, id);
21254
21607
  if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
21255
21608
  const raw = readFile2(ini.filePath);
21256
- const { data, content } = matter16(raw, {});
21609
+ const { data, content } = matter17(raw, {});
21257
21610
  if (patch.title !== void 0) {
21258
21611
  if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
21259
21612
  data.title = patch.title.trim();
@@ -21263,7 +21616,7 @@ function updateInitiative(dir, id, patch) {
21263
21616
  if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
21264
21617
  if (patch.domains !== void 0) data.domains = patch.domains;
21265
21618
  if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
21266
- const newContent = matter16.stringify(content, data);
21619
+ const newContent = matter17.stringify(content, data);
21267
21620
  writeFile2(ini.filePath, newContent);
21268
21621
  const updated = parseInitiative2(dir, ini.filePath, newContent);
21269
21622
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
@@ -21281,10 +21634,10 @@ function addExternalLink(dir, id, link) {
21281
21634
  throw new InitiativeWriteError(`External link ${link.integration}:${link.externalId} already exists on ${id}.`);
21282
21635
  }
21283
21636
  const raw = readFile2(ini.filePath);
21284
- const { data, content } = matter16(raw, {});
21637
+ const { data, content } = matter17(raw, {});
21285
21638
  const existing = Array.isArray(data.external_links) ? data.external_links : [];
21286
21639
  data.external_links = [...existing, externalLinkToData(link)];
21287
- const newContent = matter16.stringify(content, data);
21640
+ const newContent = matter17.stringify(content, data);
21288
21641
  writeFile2(ini.filePath, newContent);
21289
21642
  const updated = parseInitiative2(dir, ini.filePath, newContent);
21290
21643
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding an external link.");
@@ -21353,7 +21706,7 @@ function buildWorkItemContent(wiId, iniId, candidate) {
21353
21706
  "_What did we learn? Update after completion._",
21354
21707
  ""
21355
21708
  ].join("\n");
21356
- return matter16.stringify("\n" + body, data);
21709
+ return matter17.stringify("\n" + body, data);
21357
21710
  }
21358
21711
  function materializeCandidate(dir, id, candidateId) {
21359
21712
  const ini = getInitiative(dir, id);
@@ -21370,13 +21723,13 @@ function materializeCandidate(dir, id, candidateId) {
21370
21723
  ensureDir2(join3(dir, DRAFT_DIR));
21371
21724
  writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
21372
21725
  const raw = readFile2(ini.filePath);
21373
- const { data, content } = matter16(raw, {});
21726
+ const { data, content } = matter17(raw, {});
21374
21727
  const cands = Array.isArray(data.candidates) ? data.candidates : [];
21375
21728
  for (const c of cands) {
21376
21729
  if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
21377
21730
  }
21378
21731
  data.candidates = cands;
21379
- const newContent = matter16.stringify(content, data);
21732
+ const newContent = matter17.stringify(content, data);
21380
21733
  writeFile2(ini.filePath, newContent);
21381
21734
  const updated = parseInitiative2(dir, ini.filePath, newContent);
21382
21735
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
@@ -21490,7 +21843,7 @@ function evaluateInitiativeCompletion(dir, ini) {
21490
21843
  // ../cli/src/core/resource-write.ts
21491
21844
  import fs6 from "fs";
21492
21845
  import path5 from "path";
21493
- import matter21 from "gray-matter";
21846
+ import matter23 from "gray-matter";
21494
21847
  var RESOURCES_DIR = "knowledge/tech/resources";
21495
21848
  var ResourceWriteError = class extends Error {
21496
21849
  constructor(code, message) {
@@ -21600,7 +21953,7 @@ function buildFrontmatter(id, input, base = {}) {
21600
21953
  return data;
21601
21954
  }
21602
21955
  function serialize(data, body) {
21603
- return matter21.stringify(`
21956
+ return matter23.stringify(`
21604
21957
  ${body.trim()}
21605
21958
  `, data);
21606
21959
  }
@@ -21640,7 +21993,7 @@ function updateResource(dir, id, input) {
21640
21993
  throw new ResourceWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
21641
21994
  }
21642
21995
  const raw = readFile(match.filePath);
21643
- const parsed = matter21(raw);
21996
+ const parsed = matter23(raw);
21644
21997
  const base = parsed.data ?? {};
21645
21998
  const data = buildFrontmatter(match.id || id, input, base);
21646
21999
  let body = parsed.content.trim();
@@ -21873,7 +22226,7 @@ var CANDIDATE_DISCOVERY_GUIDANCE2 = [
21873
22226
  // ../cli/src/core/deprecation-write.ts
21874
22227
  import fs7 from "fs";
21875
22228
  import path6 from "path";
21876
- import matter23 from "gray-matter";
22229
+ import matter24 from "gray-matter";
21877
22230
  var DeprecationWriteError = class extends Error {
21878
22231
  constructor(code, message) {
21879
22232
  super(message);
@@ -21891,7 +22244,7 @@ function atomicWrite2(filePath, content) {
21891
22244
  fs7.renameSync(tmp, filePath);
21892
22245
  }
21893
22246
  function serialize3(data, body) {
21894
- return matter23.stringify(`
22247
+ return matter24.stringify(`
21895
22248
  ${body.trim()}
21896
22249
  `, data);
21897
22250
  }
@@ -21957,7 +22310,7 @@ function validateTargetInput(dir, raw) {
21957
22310
  function loadPlanRaw(dir, planId) {
21958
22311
  const match = allPlanArtifacts(dir).find((a2) => a2.id === planId || a2.title === planId);
21959
22312
  if (!match) throw new DeprecationWriteError("NOT_FOUND", `Deprecation Plan "${planId}" not found.`);
21960
- const parsed = matter23(readFile(match.filePath));
22313
+ const parsed = matter24(readFile(match.filePath));
21961
22314
  const data = { ...parsed.data ?? {} };
21962
22315
  return { filePath: match.filePath, data, body: parsed.content.trim() };
21963
22316
  }
@@ -22145,12 +22498,12 @@ function markWorkItemReady(root, id, confirm2) {
22145
22498
  }
22146
22499
  const raw = readText(root, item.path);
22147
22500
  if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
22148
- const parsed = matter24(raw);
22501
+ const parsed = matter25(raw);
22149
22502
  const data = { ...parsed.data };
22150
22503
  const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
22151
22504
  data.status = "ready";
22152
22505
  data.ready_at = today3;
22153
- const newContent = matter24.stringify(parsed.content, data);
22506
+ const newContent = matter25.stringify(parsed.content, data);
22154
22507
  const posixPath = item.path.replace(/\\/g, "/");
22155
22508
  let newRelPath = item.path;
22156
22509
  if (posixPath.includes("/work-items/draft/")) {
@@ -22445,7 +22798,7 @@ function removeDeprecationTargetTool(root, planId, targetId, confirm2) {
22445
22798
  // src/multirepo.ts
22446
22799
  import fs8 from "fs";
22447
22800
  import path7 from "path";
22448
- import matter25 from "gray-matter";
22801
+ import matter26 from "gray-matter";
22449
22802
  import { parse as parseYaml12, stringify as stringifyYaml8 } from "yaml";
22450
22803
  var ok2 = (data) => ({ ok: true, data });
22451
22804
  var fail2 = (message) => ({ ok: false, message });
@@ -22597,7 +22950,7 @@ function validateWorkItemModulesTool(root, args) {
22597
22950
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
22598
22951
  const raw = readText(root, wi.path);
22599
22952
  if (!raw) return fail2(`Could not read Work Item file.`);
22600
- const parsed = matter25(raw);
22953
+ const parsed = matter26(raw);
22601
22954
  const data = parsed.data;
22602
22955
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
22603
22956
  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) : [] })) : [];
@@ -22650,7 +23003,7 @@ function getWorkItemContextTool(root, args) {
22650
23003
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
22651
23004
  const raw = readText(root, wi.path);
22652
23005
  if (!raw) return fail2(`Could not read Work Item file.`);
22653
- const parsed = matter25(raw);
23006
+ const parsed = matter26(raw);
22654
23007
  const data = parsed.data;
22655
23008
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
22656
23009
  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) : [] })) : [];
@@ -22736,7 +23089,7 @@ function suggestBranchStrategyTool(root, args) {
22736
23089
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
22737
23090
  const raw = readText(root, wi.path);
22738
23091
  if (!raw) return fail2(`Could not read Work Item file.`);
22739
- const parsed = matter25(raw);
23092
+ const parsed = matter26(raw);
22740
23093
  const data = parsed.data;
22741
23094
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
22742
23095
  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) : [] })) : [];
@@ -23496,14 +23849,14 @@ function systemImpactCandidatesTool(root, args) {
23496
23849
  }
23497
23850
 
23498
23851
  // ../cli/src/services/integrations.ts
23499
- import matter27 from "gray-matter";
23852
+ import matter28 from "gray-matter";
23500
23853
  import { parse as parseYaml14, stringify as stringifyYaml10 } from "yaml";
23501
23854
 
23502
23855
  // ../cli/src/core/work-item-write.ts
23503
23856
  import fs10 from "fs";
23504
23857
  import path9 from "path";
23505
23858
  import crypto4 from "crypto";
23506
- import matter26 from "gray-matter";
23859
+ import matter27 from "gray-matter";
23507
23860
  var SECTION_ORDER2 = [
23508
23861
  "Actor",
23509
23862
  "Outcome",
@@ -24551,7 +24904,7 @@ async function integrationsWorkItemTool(root, args) {
24551
24904
  }
24552
24905
 
24553
24906
  // ../cli/src/core/context-pack.ts
24554
- import matter28 from "gray-matter";
24907
+ import matter29 from "gray-matter";
24555
24908
  var CONTEXT_PACK_VERSION = "1";
24556
24909
  var ARCH_DIR4 = "knowledge";
24557
24910
  function readScanJson(dir) {
@@ -24564,7 +24917,7 @@ function readScanJson(dir) {
24564
24917
  }
24565
24918
  }
24566
24919
  function firstParagraph4(markdown) {
24567
- const body = matter28(markdown).content.trim();
24920
+ const body = matter29(markdown).content.trim();
24568
24921
  const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
24569
24922
  return para ?? "";
24570
24923
  }
@@ -25065,11 +25418,11 @@ function renderContextPack(pack) {
25065
25418
  if (mixEntries.length > 0) {
25066
25419
  parts.push("## Delivery Mix\n");
25067
25420
  parts.push("Active Work Items by type:\n");
25068
- const label = (t) => {
25421
+ const label2 = (t) => {
25069
25422
  const c = t.charAt(0).toUpperCase() + t.slice(1);
25070
25423
  return c.endsWith("s") ? c : `${c}s`;
25071
25424
  };
25072
- parts.push(mixEntries.map(([t, n]) => `- ${label(t)}: ${n}`).join("\n") + "\n");
25425
+ parts.push(mixEntries.map(([t, n]) => `- ${label2(t)}: ${n}`).join("\n") + "\n");
25073
25426
  }
25074
25427
  parts.push("## Artifacts and Ownership\n");
25075
25428
  if (knowledge.artifacts.length > 0) {
@@ -25545,8 +25898,502 @@ function renderUnderstand(plan) {
25545
25898
  return parts.join("\n");
25546
25899
  }
25547
25900
 
25901
+ // ../cli/src/core/knowledge-consolidation.ts
25902
+ import { createHash as createHash3 } from "crypto";
25903
+ import matter30 from "gray-matter";
25904
+ var KNOWLEDGE7 = "knowledge";
25905
+ var CONSOLIDATION_CATEGORIES = [
25906
+ "knowledge_redundancy",
25907
+ "unincorporated_learning",
25908
+ "knowledge_fragmentation",
25909
+ "relationship_opportunity",
25910
+ "potentially_outdated",
25911
+ "knowledge_organization"
25912
+ ];
25913
+ function toPosix8(p) {
25914
+ return p.replace(/\\/g, "/");
25915
+ }
25916
+ function relFromDir(dir, filePath) {
25917
+ const d3 = toPosix8(dir).replace(/\/+$/, "");
25918
+ const f3 = toPosix8(filePath);
25919
+ return f3.startsWith(d3 + "/") ? f3.slice(d3.length + 1) : f3;
25920
+ }
25921
+ function sha256(content) {
25922
+ return createHash3("sha256").update(content, "utf-8").digest("hex");
25923
+ }
25924
+ var SHINGLE_SIZE = 6;
25925
+ var MIN_SHARED_RUN = 6;
25926
+ function contentWords(body) {
25927
+ const words = [];
25928
+ let inFence = false;
25929
+ for (const raw of body.split(/\r?\n/)) {
25930
+ const t = raw.trim();
25931
+ if (/^```/.test(t)) {
25932
+ inFence = !inFence;
25933
+ continue;
25934
+ }
25935
+ if (inFence) continue;
25936
+ if (/^#{1,6}\s/.test(t)) continue;
25937
+ if (/^>/.test(t)) continue;
25938
+ const text3 = t.replace(/^([-*+]\s+|\d+\.\s+)/, "");
25939
+ const matched = text3.toLowerCase().match(/[a-záéíóúñü0-9]+/gi);
25940
+ if (matched) words.push(...matched);
25941
+ }
25942
+ return words;
25943
+ }
25944
+ function buildShingles(body) {
25945
+ const words = contentWords(body);
25946
+ const keys = [];
25947
+ const set = /* @__PURE__ */ new Set();
25948
+ for (let i = 0; i + SHINGLE_SIZE <= words.length; i++) {
25949
+ const key = words.slice(i, i + SHINGLE_SIZE).join(" ");
25950
+ keys.push(key);
25951
+ set.add(key);
25952
+ }
25953
+ return { words, keys, set };
25954
+ }
25955
+ function longestSharedRun(a2, b2) {
25956
+ let best = 0;
25957
+ let bestStart = -1;
25958
+ let run = 0;
25959
+ let runStart = 0;
25960
+ for (let i = 0; i < a2.keys.length; i++) {
25961
+ if (b2.set.has(a2.keys[i])) {
25962
+ if (run === 0) runStart = i;
25963
+ run += 1;
25964
+ if (run > best) {
25965
+ best = run;
25966
+ bestStart = runStart;
25967
+ }
25968
+ } else {
25969
+ run = 0;
25970
+ }
25971
+ }
25972
+ if (best === 0) return { length: 0, snippet: "" };
25973
+ const snippet = a2.words.slice(bestStart, bestStart + SHINGLE_SIZE + best - 1).join(" ");
25974
+ return { length: best, snippet };
25975
+ }
25976
+ var NORMATIVE_RE = /(^|[^a-záéíóúñü])(must|should|shall|debe[ns]?|deber[ií]a[ns]?|se\s+debe|hay\s+que|requiere[ns]?|require[sd]?|no\s+debe|invariant[e]?|invariants|restricci[oó]n|constraint|regla|obligatori[oa])([^a-záéíóúñü]|$)/i;
25977
+ var PAST_RESULT_RE = /(compil[aoeéó]\w*|compiled|benchmark\w*|passed|succeed\w*|succeeded|ran\s+successfully|ejecut\w*|resultad\w*|crates?\b|ns\/op|µs\b|throughput|latenc\w*|baseline|measured|medid\w*|\d+\s*%|\bx\d|faster|slower)/i;
25978
+ var TEST_RE = /(\btest\w*|\bpruebas?\b|coverage|cobertura|verde[s]?|green\b|assert\w*)/i;
25979
+ var BENCH_RE = /(benchmark\w*|ns\/op|µs\b|throughput|latenc\w*|ops\/s|\d+\s*%|faster|slower|baseline)/i;
25980
+ var EXPERIMENT_RE = /(experiment\w*|experiment[oa]\w*|prototyp\w*|prototip\w*|spike\b|probamos|probé|intentamos)/i;
25981
+ function splitSentences(text3) {
25982
+ return text3.replace(/\s+/g, " ").split(/(?<=[.!?;])\s+/).map((s) => s.trim()).filter(Boolean);
25983
+ }
25984
+ function classifyLearningSentence(sentence) {
25985
+ const s = sentence.trim();
25986
+ if (!s) return "none";
25987
+ const normative = NORMATIVE_RE.test(s);
25988
+ const pastResult = PAST_RESULT_RE.test(s);
25989
+ if (normative && !pastResult) return "durable-rule";
25990
+ if (BENCH_RE.test(s)) return "benchmark-evidence";
25991
+ if (pastResult) return "historical-result";
25992
+ if (TEST_RE.test(s)) return "test-observation";
25993
+ if (EXPERIMENT_RE.test(s)) return "experimental-result";
25994
+ return "none";
25995
+ }
25996
+ function firstRuleFragment(learning) {
25997
+ for (const sentence of splitSentences(learning)) {
25998
+ if (classifyLearningSentence(sentence) === "durable-rule") return sentence;
25999
+ }
26000
+ return null;
26001
+ }
26002
+ function sectionBody(body, titles) {
26003
+ const lines = body.split(/\r?\n/);
26004
+ const re = new RegExp(`^#{1,3}\\s+(${titles.join("|")})\\s*$`, "i");
26005
+ const start = lines.findIndex((l2) => re.test(l2));
26006
+ if (start < 0) return null;
26007
+ const collected = [];
26008
+ for (let i = start + 1; i < lines.length; i++) {
26009
+ if (/^#{1,3}\s+/.test(lines[i])) break;
26010
+ collected.push(lines[i]);
26011
+ }
26012
+ const text3 = collected.join("\n").trim();
26013
+ return text3.length > 0 ? text3 : null;
26014
+ }
26015
+ function stableOpportunityId(category, sources) {
26016
+ const key = [category, ...[...sources].sort()].join("|");
26017
+ return `${category}:${sha256(key).slice(0, 12)}`;
26018
+ }
26019
+ function layerOf(a2) {
26020
+ const byType = layerForType(a2.type);
26021
+ if (byType) return byType;
26022
+ const p = toPosix8(a2.filePath);
26023
+ if (p.includes(`/${KNOWLEDGE7}/business/`)) return "Business";
26024
+ if (p.includes(`/${KNOWLEDGE7}/product/`)) return "Product";
26025
+ if (p.includes(`/${KNOWLEDGE7}/tech/`)) return "Tech";
26026
+ if (p.includes(`/${KNOWLEDGE7}/delivery/`)) return "Delivery";
26027
+ return null;
26028
+ }
26029
+ function inScope(la, scope) {
26030
+ if (scope.kind === "all") return true;
26031
+ if (scope.kind === "layer") return la.layer === scope.layer;
26032
+ return la.artifact.affectedModules.includes(scope.module);
26033
+ }
26034
+ function label(la) {
26035
+ return la.artifact.id || la.rel;
26036
+ }
26037
+ function buildConsolidationPlan(dir, opts = {}) {
26038
+ requireConfig(dir);
26039
+ const scope = opts.scope ?? { kind: "all" };
26040
+ const now = opts.now ?? /* @__PURE__ */ new Date();
26041
+ const archDir = join(dir, KNOWLEDGE7);
26042
+ const rawArtifacts = exists(archDir) ? readArtifacts(archDir) : [];
26043
+ const loaded = rawArtifacts.map((a2) => {
26044
+ let body = "";
26045
+ try {
26046
+ body = matter30(readFile(a2.filePath)).content;
26047
+ } catch {
26048
+ body = "";
26049
+ }
26050
+ return { artifact: a2, rel: relFromDir(dir, a2.filePath), body, layer: layerOf(a2) };
26051
+ });
26052
+ const scoped = loaded.filter((la) => inScope(la, scope));
26053
+ const scopedRels = new Set(scoped.map((la) => la.rel));
26054
+ const knownIds = new Set(loaded.map((la) => la.artifact.id).filter(Boolean));
26055
+ const opportunities = [];
26056
+ try {
26057
+ const config = requireConfig(dir);
26058
+ const graph = buildGraph(dir, config, { scope: "all" }, now);
26059
+ const hints = buildGraphHints(dir, graph, now);
26060
+ for (const hint of hints.hints) {
26061
+ if (!hintInScope(hint, scoped)) continue;
26062
+ opportunities.push(relationshipOpportunityFromHint(hint));
26063
+ }
26064
+ } catch {
26065
+ }
26066
+ for (const la of scoped) {
26067
+ const dangling = la.artifact.decisions.filter((d3) => d3 && !knownIds.has(d3) && !decisionFileExists(dir, d3));
26068
+ if (dangling.length === 0) continue;
26069
+ opportunities.push({
26070
+ id: stableOpportunityId("potentially_outdated", [label(la), ...dangling]),
26071
+ category: "potentially_outdated",
26072
+ classification: "observed",
26073
+ confidence: "requires-review",
26074
+ title: `${label(la)} references decision(s) that no longer exist`,
26075
+ source_artifacts: [label(la)],
26076
+ target_artifacts: [label(la)],
26077
+ evidence: dangling.map((d3) => `Declared decision \`${d3}\` has no matching artifact in the project.`),
26078
+ rationale: "A reference to a missing decision is a verifiable inconsistency: either the decision artifact was removed/renamed or the reference is stale.",
26079
+ possible_impact: "Broken traceability between a Work Item/artifact and its decisions.",
26080
+ suggested_action: "Review and update or remove the dangling decision reference.",
26081
+ requires_semantic_review: false
26082
+ });
26083
+ }
26084
+ const comparable = scoped.filter(isComparableKnowledge);
26085
+ const shingleIndex = /* @__PURE__ */ new Map();
26086
+ const shinglesByRel = /* @__PURE__ */ new Map();
26087
+ for (const la of comparable) {
26088
+ const shingles = buildShingles(la.body);
26089
+ shinglesByRel.set(la.rel, shingles);
26090
+ for (const key of shingles.set) {
26091
+ const arr = shingleIndex.get(key) ?? [];
26092
+ arr.push(la);
26093
+ shingleIndex.set(key, arr);
26094
+ }
26095
+ }
26096
+ const candidatePairs = /* @__PURE__ */ new Map();
26097
+ for (const arts of shingleIndex.values()) {
26098
+ if (arts.length < 2) continue;
26099
+ for (let i = 0; i < arts.length; i++) {
26100
+ for (let j2 = i + 1; j2 < arts.length; j2++) {
26101
+ if (arts[i].rel === arts[j2].rel) continue;
26102
+ const key = [arts[i].rel, arts[j2].rel].sort().join("||");
26103
+ if (!candidatePairs.has(key)) candidatePairs.set(key, { a: arts[i], b: arts[j2] });
26104
+ }
26105
+ }
26106
+ }
26107
+ for (const { a: a2, b: b2 } of candidatePairs.values()) {
26108
+ const run = longestSharedRun(shinglesByRel.get(a2.rel), shinglesByRel.get(b2.rel));
26109
+ if (run.length < MIN_SHARED_RUN) continue;
26110
+ const passageWords = SHINGLE_SIZE + run.length - 1;
26111
+ opportunities.push({
26112
+ id: stableOpportunityId("knowledge_redundancy", [label(a2), label(b2)]),
26113
+ category: "knowledge_redundancy",
26114
+ classification: "suggested",
26115
+ confidence: "requires-review",
26116
+ title: `Possible overlapping content between ${label(a2)} and ${label(b2)}`,
26117
+ source_artifacts: [label(a2), label(b2)],
26118
+ target_artifacts: [label(a2), label(b2)],
26119
+ evidence: [
26120
+ `A ${passageWords}-word passage appears verbatim in both artifacts: "${redactSecrets3(run.snippet)}".`
26121
+ ],
26122
+ rationale: "A verbatim shared passage is a hint of potential duplication, NOT proof. Two documents can legitimately restate context from different layers/responsibilities; shared template structure alone was NOT used.",
26123
+ possible_impact: "If genuinely duplicated, the same fact can drift out of sync across files.",
26124
+ suggested_action: "Semantic review: compare the overlapping passage; if one restates the other, keep a single reference location and link the rest. Preserve layer separation.",
26125
+ requires_semantic_review: true
26126
+ });
26127
+ }
26128
+ for (const la of scoped) {
26129
+ if (!isCompletedWorkItem(la)) continue;
26130
+ const learning = sectionBody(la.body, ["Learning", "Aprendizaje", "Aprendizajes", "Learnings"]);
26131
+ if (!learning) continue;
26132
+ const ruleFragment = firstRuleFragment(learning);
26133
+ if (!ruleFragment) continue;
26134
+ opportunities.push({
26135
+ id: stableOpportunityId("unincorporated_learning", [label(la)]),
26136
+ category: "unincorporated_learning",
26137
+ classification: "suggested",
26138
+ confidence: "requires-review",
26139
+ title: `${label(la)} captured a learning that may belong in current knowledge`,
26140
+ source_artifacts: [label(la)],
26141
+ target_artifacts: [],
26142
+ // unresolved — determined in semantic review (never invent a destination)
26143
+ evidence: [`Completed Work Item records a rule-like learning (heuristic classification): "${redactSecrets3(ruleFragment)}"`],
26144
+ rationale: "A sentence phrased as a durable rule MAY represent live technical knowledge not yet reflected in current knowledge. The rule/result distinction is a heuristic, not a verified conclusion \u2014 this is a candidate, not a confirmed gap.",
26145
+ possible_impact: "A relevant constraint stays buried in delivery history instead of informing current work.",
26146
+ suggested_action: "Semantic review: confirm whether this is a live rule and determine the right destination artifact; if so, incorporate it WITH a reference back to the Work Item. Do not delete or summarize the Work Item.",
26147
+ requires_semantic_review: true
26148
+ });
26149
+ }
26150
+ const weakByFolder = /* @__PURE__ */ new Map();
26151
+ for (const la of scoped) {
26152
+ if (!isConsolidatableKnowledge(la)) continue;
26153
+ const quality = safeQuality(dir, la.rel);
26154
+ if (quality === "placeholder" || quality === "weak") {
26155
+ opportunities.push({
26156
+ id: stableOpportunityId("knowledge_organization", [la.rel]),
26157
+ category: "knowledge_organization",
26158
+ classification: "observed",
26159
+ confidence: "requires-review",
26160
+ title: `${label(la)} is ${quality} and may need consolidation or expansion`,
26161
+ source_artifacts: [label(la)],
26162
+ target_artifacts: [label(la)],
26163
+ evidence: [`Artifact quality analysis classifies this canonical knowledge artifact as "${quality}".`],
26164
+ rationale: "Observed: the quality heuristic rates this artifact low-signal. Whether it should be expanded, merged or cross-referenced is a judgement that needs semantic review \u2014 the quality signal alone does not decide it.",
26165
+ possible_impact: "Readers must open more files to assemble a complete picture.",
26166
+ suggested_action: "Semantic review: decide whether to expand, merge into a stronger artifact, or cross-reference it.",
26167
+ requires_semantic_review: true
26168
+ });
26169
+ const folder = la.rel.split("/").slice(0, -1).join("/");
26170
+ const arr = weakByFolder.get(folder) ?? [];
26171
+ arr.push(la);
26172
+ weakByFolder.set(folder, arr);
26173
+ }
26174
+ }
26175
+ for (const [folder, arts] of weakByFolder) {
26176
+ if (arts.length < 3) continue;
26177
+ opportunities.push({
26178
+ id: stableOpportunityId("knowledge_fragmentation", arts.map((a2) => a2.rel)),
26179
+ category: "knowledge_fragmentation",
26180
+ classification: "suggested",
26181
+ confidence: "requires-review",
26182
+ title: `${folder}/ holds ${arts.length} low-signal canonical artifacts \u2014 possible fragmentation`,
26183
+ source_artifacts: arts.map((a2) => label(a2)),
26184
+ target_artifacts: [folder],
26185
+ evidence: [`${arts.length} placeholder/weak canonical knowledge artifacts in the same folder: ${arts.map((a2) => a2.rel.split("/").pop()).join(", ")}.`],
26186
+ rationale: "Several thin canonical artifacts in one folder MAY be easier to consume if consolidated or cross-linked. Consolidation must preserve each distinct responsibility; Work Items and generated/lifecycle artifacts are not considered here.",
26187
+ possible_impact: "Related knowledge is spread across too many files to follow.",
26188
+ suggested_action: "Semantic review: evaluate whether cross-references or a consolidation would add clarity. Do not merge by file count alone.",
26189
+ requires_semantic_review: true
26190
+ });
26191
+ }
26192
+ opportunities.sort((x2, y) => {
26193
+ const ci = CONSOLIDATION_CATEGORIES.indexOf(x2.category) - CONSOLIDATION_CATEGORIES.indexOf(y.category);
26194
+ return ci !== 0 ? ci : x2.id.localeCompare(y.id);
26195
+ });
26196
+ const seen = /* @__PURE__ */ new Set();
26197
+ const deduped = opportunities.filter((o2) => seen.has(o2.id) ? false : (seen.add(o2.id), true));
26198
+ const layers = discoverLayers(dir);
26199
+ const layersEvaluated = scope.kind === "layer" ? [scope.layer] : layers.map((l2) => l2.layer);
26200
+ const workItemsEvaluated = scoped.filter(isWorkItem).length;
26201
+ const sourceFiles = [...scopedRels].sort().map((rel) => ({ path: rel, hash: sha256(readFileSafe(join(dir, rel))) }));
26202
+ const byCategory = Object.fromEntries(
26203
+ CONSOLIDATION_CATEGORIES.map((c) => [c, deduped.filter((o2) => o2.category === c).length])
26204
+ );
26205
+ return {
26206
+ generated_at: now.toISOString(),
26207
+ scope: describeScope(scope),
26208
+ project_context: reportingProjectContext(dir),
26209
+ coverage: {
26210
+ scope: describeScope(scope),
26211
+ layers_evaluated: layersEvaluated,
26212
+ artifacts_evaluated: scoped.length,
26213
+ work_items_evaluated: workItemsEvaluated,
26214
+ excluded_sources: scope.kind === "all" ? [] : [...loaded.map((l2) => l2.rel)].filter((r2) => !scopedRels.has(r2)).sort()
26215
+ },
26216
+ source_files: sourceFiles,
26217
+ opportunities: deduped,
26218
+ summary: {
26219
+ total: deduped.length,
26220
+ by_category: byCategory,
26221
+ requires_semantic_review: deduped.filter((o2) => o2.requires_semantic_review).length
26222
+ }
26223
+ };
26224
+ }
26225
+ var CATEGORY_LABELS = {
26226
+ knowledge_redundancy: "Possible redundancy",
26227
+ unincorporated_learning: "Learning integration opportunity",
26228
+ knowledge_fragmentation: "Possible fragmentation",
26229
+ relationship_opportunity: "Relationship review hint",
26230
+ potentially_outdated: "Potential inconsistency",
26231
+ knowledge_organization: "Organization opportunity"
26232
+ };
26233
+ function renderConsolidationMarkdown(plan) {
26234
+ const lines = [];
26235
+ lines.push("# Knowledge Consolidation Plan");
26236
+ lines.push("");
26237
+ lines.push("> Derived, review-only projection. No canonical knowledge was modified by this analysis.");
26238
+ lines.push("");
26239
+ lines.push(`- Generated at: ${plan.generated_at}`);
26240
+ lines.push(`- Scope: ${plan.scope}`);
26241
+ lines.push(`- Project: state=${plan.project_context.state}, mode=${plan.project_context.mode}, structure=${plan.project_context.repository_structure}`);
26242
+ lines.push("");
26243
+ lines.push("## Coverage");
26244
+ lines.push("");
26245
+ lines.push(`- Layers evaluated: ${plan.coverage.layers_evaluated.join(", ") || "\u2014"}`);
26246
+ lines.push(`- Artifacts evaluated: ${plan.coverage.artifacts_evaluated}`);
26247
+ lines.push(`- Work Items evaluated: ${plan.coverage.work_items_evaluated}`);
26248
+ if (plan.coverage.excluded_sources.length > 0) {
26249
+ lines.push(`- Excluded from this scope: ${plan.coverage.excluded_sources.length} artifact(s)`);
26250
+ }
26251
+ lines.push("");
26252
+ lines.push("## Findings");
26253
+ lines.push("");
26254
+ if (plan.summary.total === 0) {
26255
+ lines.push("No consolidation opportunities were detected by the available deterministic analyses within this scope.");
26256
+ lines.push("");
26257
+ lines.push("This is not a claim that the knowledge is perfect \u2014 only that no opportunity was found with the current signals.");
26258
+ lines.push("");
26259
+ return lines.join("\n") + "\n";
26260
+ }
26261
+ for (const cat of CONSOLIDATION_CATEGORIES) {
26262
+ const n = plan.summary.by_category[cat];
26263
+ if (n > 0) lines.push(`- ${n} \xD7 ${CATEGORY_LABELS[cat]}`);
26264
+ }
26265
+ lines.push("");
26266
+ lines.push(`${plan.summary.requires_semantic_review} finding(s) require semantic (agent/human) review before acting.`);
26267
+ lines.push("");
26268
+ lines.push("## Opportunities");
26269
+ lines.push("");
26270
+ for (const o2 of plan.opportunities) {
26271
+ lines.push(`### ${o2.title}`);
26272
+ lines.push("");
26273
+ lines.push(`- Id: \`${o2.id}\``);
26274
+ lines.push(`- Category: ${o2.category}`);
26275
+ lines.push(`- Classification: **${o2.classification}**${o2.requires_semantic_review ? " (requires semantic review)" : ""}`);
26276
+ lines.push(`- Confidence: ${o2.confidence}`);
26277
+ lines.push(`- Source: ${o2.source_artifacts.join(", ") || "\u2014"}`);
26278
+ lines.push(
26279
+ `- Suggested target: ${o2.target_artifacts.length > 0 ? o2.target_artifacts.join(", ") : "(unresolved \u2014 to be determined in semantic review)"}`
26280
+ );
26281
+ lines.push(`- Evidence:`);
26282
+ for (const e of o2.evidence) lines.push(` - ${e}`);
26283
+ lines.push(`- Rationale: ${o2.rationale}`);
26284
+ lines.push(`- Possible impact: ${o2.possible_impact}`);
26285
+ lines.push(`- Suggested action: ${o2.suggested_action}`);
26286
+ lines.push("");
26287
+ }
26288
+ return lines.join("\n") + "\n";
26289
+ }
26290
+ function serializeConsolidationJson(plan) {
26291
+ return JSON.stringify(plan, null, 2) + "\n";
26292
+ }
26293
+ function describeScope(scope) {
26294
+ if (scope.kind === "all") return "all";
26295
+ if (scope.kind === "layer") return `layer:${scope.layer}`;
26296
+ return `module:${scope.module}`;
26297
+ }
26298
+ function isWorkItem(la) {
26299
+ return toPosix8(la.artifact.filePath).includes("/delivery/work-items/");
26300
+ }
26301
+ function isCompletedWorkItem(la) {
26302
+ const p = toPosix8(la.artifact.filePath);
26303
+ return p.includes("/delivery/work-items/completed/") || isWorkItem(la) && la.artifact.status === "completed";
26304
+ }
26305
+ function isScaffolding(la) {
26306
+ const p = toPosix8(la.artifact.filePath);
26307
+ return p.includes(`/${KNOWLEDGE7}/agents/`) || p.includes(`/${KNOWLEDGE7}/skills/`) || p.includes(`/${KNOWLEDGE7}/external/`) || la.artifact.type === "knowledge-capsule";
26308
+ }
26309
+ var NON_KNOWLEDGE_DOC_TYPES = /* @__PURE__ */ new Set([
26310
+ "roadmap",
26311
+ "initiative",
26312
+ "deprecation",
26313
+ "knowledge-capsule",
26314
+ "inventory",
26315
+ "project-resource"
26316
+ ]);
26317
+ function isComparableKnowledge(la) {
26318
+ return !isWorkItem(la) && la.artifact.type !== "roadmap" && !isScaffolding(la);
26319
+ }
26320
+ function isConsolidatableKnowledge(la) {
26321
+ if (isWorkItem(la) || isScaffolding(la)) return false;
26322
+ if (NON_KNOWLEDGE_DOC_TYPES.has(la.artifact.type)) return false;
26323
+ return la.layer === "Business" || la.layer === "Product" || la.layer === "Tech";
26324
+ }
26325
+ var SECRET_TOKEN_PATTERNS = [
26326
+ /\bgh[pours]_[A-Za-z0-9]{16,}\b/g,
26327
+ // GitHub tokens
26328
+ /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
26329
+ // AWS access key ids
26330
+ /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,
26331
+ // Slack tokens
26332
+ /\bsk-[A-Za-z0-9]{16,}\b/g,
26333
+ // OpenAI-style keys
26334
+ /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,
26335
+ // JWTs
26336
+ /\b[A-Za-z0-9+/]{40,}={0,2}\b/g
26337
+ // long base64 / opaque blobs
26338
+ ];
26339
+ function redactSecrets3(text3) {
26340
+ let out = text3;
26341
+ for (const re of SECRET_TOKEN_PATTERNS) out = out.replace(re, "[redacted]");
26342
+ return out;
26343
+ }
26344
+ function safeQuality(dir, rel) {
26345
+ try {
26346
+ return analyzeKnowledgeArtifact(dir, rel);
26347
+ } catch {
26348
+ return "useful";
26349
+ }
26350
+ }
26351
+ function readFileSafe(abs) {
26352
+ try {
26353
+ return readFile(abs);
26354
+ } catch {
26355
+ return "";
26356
+ }
26357
+ }
26358
+ function decisionFileExists(dir, decisionId) {
26359
+ const decisionsDir = join(dir, KNOWLEDGE7, "tech", "decisions");
26360
+ if (!exists(decisionsDir)) return false;
26361
+ try {
26362
+ return readDir(decisionsDir).some((e) => {
26363
+ const full = join(decisionsDir, e);
26364
+ if (!isFile(full) || !e.endsWith(".md")) return false;
26365
+ return e.toLowerCase().includes(decisionId.toLowerCase());
26366
+ });
26367
+ } catch {
26368
+ return false;
26369
+ }
26370
+ }
26371
+ function hintInScope(hint, scoped) {
26372
+ if (scoped.length === 0) return false;
26373
+ return scoped.some(
26374
+ (la) => la.artifact.id === hint.artifact_id || hint.path && toPosix8(la.artifact.filePath).endsWith(toPosix8(hint.path))
26375
+ );
26376
+ }
26377
+ function relationshipOpportunityFromHint(hint) {
26378
+ const finding = graphHintFinding(hint);
26379
+ return {
26380
+ id: stableOpportunityId("relationship_opportunity", [hint.artifact_id, ...hint.missing]),
26381
+ category: "relationship_opportunity",
26382
+ classification: "observed",
26383
+ confidence: "requires-review",
26384
+ title: hint.message,
26385
+ source_artifacts: [hint.artifact_id],
26386
+ target_artifacts: [hint.artifact_id],
26387
+ evidence: [finding.evidence],
26388
+ rationale: "A missing OPTIONAL relationship is an opportunity to review, not an error or a verified inconsistency. Kaddo never invents the relationship.",
26389
+ possible_impact: "Weaker traceability in the knowledge graph.",
26390
+ suggested_action: finding.suggested_action,
26391
+ requires_semantic_review: false
26392
+ };
26393
+ }
26394
+
25548
26395
  // src/generate.ts
25549
- function requireConfig(root) {
26396
+ function requireConfig2(root) {
25550
26397
  const config = loadConfig(root);
25551
26398
  if (!config) throw new KaddoMcpError("Kaddo project not found. Run `kaddo init` first.");
25552
26399
  return config;
@@ -25560,7 +26407,7 @@ function slug6(s) {
25560
26407
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
25561
26408
  }
25562
26409
  function generateContext(root) {
25563
- const config = requireConfig(root);
26410
+ const config = requireConfig2(root);
25564
26411
  requireKnowledge(root);
25565
26412
  const pack = buildContextPack(root, config);
25566
26413
  writeDerived(root, ".kaddo/context-pack.md", renderContextPack(pack));
@@ -25574,7 +26421,7 @@ function generateContext(root) {
25574
26421
  };
25575
26422
  }
25576
26423
  function generateExplain(root) {
25577
- requireConfig(root);
26424
+ requireConfig2(root);
25578
26425
  requireKnowledge(root);
25579
26426
  const exp = buildProjectExplanation(root);
25580
26427
  writeDerived(root, ".kaddo/explain.md", renderExplanationHuman(exp));
@@ -25588,7 +26435,7 @@ function generateExplain(root) {
25588
26435
  };
25589
26436
  }
25590
26437
  function generateUnderstand(root) {
25591
- const config = requireConfig(root);
26438
+ const config = requireConfig2(root);
25592
26439
  requireKnowledge(root);
25593
26440
  const plan = buildUnderstandPlan(root, config);
25594
26441
  writeDerived(root, ".kaddo/understand.md", renderUnderstand(plan));
@@ -25601,7 +26448,7 @@ function generateUnderstand(root) {
25601
26448
  };
25602
26449
  }
25603
26450
  function generateGraph(root, scope = "active") {
25604
- const config = requireConfig(root);
26451
+ const config = requireConfig2(root);
25605
26452
  requireKnowledge(root);
25606
26453
  const graph = buildGraph(root, config, { scope });
25607
26454
  const hints = buildGraphHints(root, graph);
@@ -25623,8 +26470,39 @@ function generateGraph(root, scope = "active") {
25623
26470
  next_suggested_resources: ["kaddo://graph", "kaddo://graph-hints"]
25624
26471
  };
25625
26472
  }
26473
+ var LAYER_BY_FLAG = {
26474
+ business: "Business",
26475
+ product: "Product",
26476
+ tech: "Tech",
26477
+ delivery: "Delivery"
26478
+ };
26479
+ function generateConsolidationPlan(root, opts = {}) {
26480
+ requireConfig2(root);
26481
+ requireKnowledge(root);
26482
+ let scope = { kind: "all" };
26483
+ if (opts.module) {
26484
+ scope = { kind: "module", module: opts.module };
26485
+ } else if (opts.layer) {
26486
+ const layer2 = LAYER_BY_FLAG[opts.layer.toLowerCase()];
26487
+ if (!layer2) {
26488
+ throw new KaddoMcpError(`Unknown layer "${opts.layer}". Use one of: business, product, tech, delivery.`);
26489
+ }
26490
+ scope = { kind: "layer", layer: layer2 };
26491
+ }
26492
+ const plan = buildConsolidationPlan(root, { scope });
26493
+ writeDerived(root, ".kaddo/consolidation-plan.md", renderConsolidationMarkdown(plan));
26494
+ writeDerived(root, ".kaddo/consolidation-plan.json", serializeConsolidationJson(plan));
26495
+ const warnings = plan.summary.requires_semantic_review > 0 ? [`${plan.summary.requires_semantic_review} finding(s) require semantic review before acting \u2014 nothing was changed automatically.`] : [];
26496
+ return {
26497
+ status: "ok",
26498
+ files_written: [".kaddo/consolidation-plan.md", ".kaddo/consolidation-plan.json"],
26499
+ summary: `Consolidation Plan generated (scope: ${plan.scope}, ${plan.summary.total} opportunity/opportunities).`,
26500
+ warnings,
26501
+ next_suggested_resources: ["kaddo://consolidation-plan"]
26502
+ };
26503
+ }
25626
26504
  function generateImpactReport(root, opts = {}) {
25627
- requireConfig(root);
26505
+ requireConfig2(root);
25628
26506
  requireKnowledge(root);
25629
26507
  const format = opts.format ?? "markdown";
25630
26508
  const report = buildImpactReport(root, { scope: opts.scope ?? "all", scopeSource: opts.scope ? "explicit" : "default" });
@@ -25640,7 +26518,7 @@ function generateImpactReport(root, opts = {}) {
25640
26518
  };
25641
26519
  }
25642
26520
  function generateSavingsReport(root, opts = {}) {
25643
- requireConfig(root);
26521
+ requireConfig2(root);
25644
26522
  requireKnowledge(root);
25645
26523
  const format = opts.format ?? "markdown";
25646
26524
  const report = buildSavingsReport(root, { scope: opts.scope ?? "all", scopeSource: opts.scope ? "explicit" : "default" });
@@ -25656,7 +26534,7 @@ function generateSavingsReport(root, opts = {}) {
25656
26534
  };
25657
26535
  }
25658
26536
  function generateDriftReport(root, opts = {}) {
25659
- requireConfig(root);
26537
+ requireConfig2(root);
25660
26538
  const format = opts.format ?? "markdown";
25661
26539
  const report = buildDriftReport(root);
25662
26540
  const content = format === "json" ? serializeDriftJson(report) : renderDriftMarkdown(report);
@@ -25671,7 +26549,7 @@ function generateDriftReport(root, opts = {}) {
25671
26549
  };
25672
26550
  }
25673
26551
  function generateQuestionsReport(root, opts = {}) {
25674
- requireConfig(root);
26552
+ requireConfig2(root);
25675
26553
  const format = opts.format ?? "markdown";
25676
26554
  const report = buildOpenQuestionsReport(root);
25677
26555
  const content = format === "json" ? serializeOpenQuestionsJson(report) : renderOpenQuestionsMarkdown(report);
@@ -25686,7 +26564,7 @@ function generateQuestionsReport(root, opts = {}) {
25686
26564
  };
25687
26565
  }
25688
26566
  function generateCapsuleDraft(root) {
25689
- const config = requireConfig(root);
26567
+ const config = requireConfig2(root);
25690
26568
  requireKnowledge(root);
25691
26569
  const capsule = buildCapsule(root, config);
25692
26570
  const name = slug6(config.project.name);
@@ -25906,13 +26784,13 @@ async function guardedAsync(root, fn) {
25906
26784
  throw err;
25907
26785
  }
25908
26786
  }
25909
- function generated(root, label, fn) {
26787
+ function generated(root, label2, fn) {
25910
26788
  try {
25911
26789
  assertKaddoProject(root);
25912
26790
  const result = fn();
25913
26791
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25914
26792
  } catch (err) {
25915
- const message = err instanceof KaddoMcpError ? err.message : `Could not ${label}. ${String(err)}`;
26793
+ const message = err instanceof KaddoMcpError ? err.message : `Could not ${label2}. ${String(err)}`;
25916
26794
  return { content: [{ type: "text", text: message }], isError: true };
25917
26795
  }
25918
26796
  }
@@ -26613,6 +27491,19 @@ function createServer(root) {
26613
27491
  },
26614
27492
  async (args) => generated(root, "generate knowledge graph", () => generateGraph(root, args.scope ?? "active"))
26615
27493
  );
27494
+ server.registerTool(
27495
+ "kaddo_generate_consolidation_plan",
27496
+ {
27497
+ title: "Generate knowledge consolidation plan",
27498
+ description: `Analyze canonical knowledge and write .kaddo/consolidation-plan.md/.json \u2014 a reviewable projection of consolidation opportunities (redundancy, unincorporated learning, fragmentation, relationships, potential inconsistencies, organization). Deterministic: facts are "observed"; anything needing judgement is a "suggested" candidate flagged for semantic review. Optional layer (business|product|tech|delivery) or module scope. ${DERIVED_NOTE}`,
27499
+ inputSchema: { layer: z4.string().optional(), module: z4.string().optional() }
27500
+ },
27501
+ async (args) => generated(
27502
+ root,
27503
+ "generate consolidation plan",
27504
+ () => generateConsolidationPlan(root, { layer: args.layer, module: args.module })
27505
+ )
27506
+ );
26616
27507
  server.registerTool(
26617
27508
  "kaddo_generate_impact_report",
26618
27509
  {