@kaddo/cli 3.17.0 → 3.19.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 (3) hide show
  1. package/README.md +7 -1
  2. package/dist/index.js +386 -39
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -454,7 +454,12 @@ Nodes come from knowledge layers, Work Items, code globs, capabilities, ADRs and
454
454
  Capsules; edges come from front matter (`code`, `capabilities`, `decisions`, `source_id`,
455
455
  `source_initiative`) and the external registry. It never reads `src/`, never reads source code
456
456
  and never calls an LLM. `kaddo explain` and `kaddo context` show a graph **summary** once it has
457
- been exported (they never generate it). See the
457
+ been exported (they never generate it).
458
+
459
+ Every export also rates **relationship quality** and writes non-blocking metadata hints
460
+ (`.kaddo/graph-hints.md` + `.json`) — detecting active Work Items without `code`/`capabilities`,
461
+ ADRs without governed `code`, capabilities/capsules with no Work Item link, and more. The
462
+ `graph-agent` turns those hints into precise front matter you confirm and apply. See the
458
463
  [Knowledge Graph Export guide](https://kaddo.trycatch.tv/knowledge-graph-export/).
459
464
 
460
465
  ## Roadmap
@@ -499,6 +504,7 @@ create --from roadmap → owners → guard → explain`.
499
504
  | v3.15 | Delivery context consistency: phase-based handoff + per-phase LLM instructions; assisted `owners suggest` (normalize/validate globs); new `ownership-agent`; guard untracked-files warning; duplicate Work Item detection |
500
505
  | v3.16 | Knowledge Capsules: `kaddo capsule export/add`, External Knowledge in context/explain, new `capsule-agent` |
501
506
  | v3.17 | Knowledge Graph Export: `kaddo graph export` (`.kaddo/graph.json` + `.mmd`, `--scope`/`--format`); graph summary in context/explain |
507
+ | v3.18 | Graph relationship quality & metadata hints: `graph-hints.md`/`.json`, quality levels, `graph-agent`; hints in context/explain/understand |
502
508
 
503
509
  **Optional modules (installed with `kaddo add`):**
504
510
 
package/dist/index.js CHANGED
@@ -1652,6 +1652,19 @@ var RESPONSIBILITY_MATRIX = {
1652
1652
  ],
1653
1653
  next: ["kaddo capsule export"]
1654
1654
  },
1655
+ "graph-agent": {
1656
+ agent: "graph-agent",
1657
+ responsibleFor: ["Reviewing graph hints", "Proposing precise relationship front matter"],
1658
+ produces: ["proposed front matter (code/capabilities/decisions/source/capsules)"],
1659
+ canSuggest: ["kaddo graph export", "kaddo owners suggest"],
1660
+ cannotSuggest: [
1661
+ "code",
1662
+ "git",
1663
+ "modifying files without confirmation",
1664
+ "inventing relationships"
1665
+ ],
1666
+ next: ["kaddo graph export"]
1667
+ },
1655
1668
  "ownership-agent": {
1656
1669
  agent: "ownership-agent",
1657
1670
  responsibleFor: ["Precise code: ownership for Work Items and artifacts"],
@@ -3207,6 +3220,74 @@ edit the roadmap yourself).
3207
3220
  - Duplicates, overlaps and dependencies are flagged.
3208
3221
  - The response ends with a human-decision handoff \u2014 no agent is auto-executed.
3209
3222
  `;
3223
+ var GRAPH_AGENT = `# Graph Agent
3224
+
3225
+ ## Role
3226
+
3227
+ You are the Kaddo Graph Agent. Your job is to review the **graph hints** produced by
3228
+ \`kaddo graph export\` and propose **precise relationship front matter** so the knowledge graph
3229
+ becomes more connected and useful.
3230
+
3231
+ You do not write code, you do not modify files, and you never run Git. You propose; the human
3232
+ confirms and edits the artifact front matter, then re-runs \`kaddo graph export\`.
3233
+
3234
+ ## When to Use
3235
+
3236
+ Use this agent when \`kaddo graph export\` reports relationship quality \`partial\`, \`sparse\` or
3237
+ \`empty\`, or when \`kaddo understand\` recommends reviewing graph hints during Active Delivery.
3238
+
3239
+ ## Input Required
3240
+
3241
+ Provide \`.kaddo/context-pack.md\`, \`.kaddo/graph.json\` and \`.kaddo/graph-hints.md\` as the primary
3242
+ inputs, plus the Work Items under \`knowledge/delivery/work-items/\`, the ADRs under
3243
+ \`knowledge/tech/decisions/\` and \`knowledge/product/capabilities.md\` when they exist.
3244
+
3245
+ ## Expected Output
3246
+
3247
+ For each hint, a concrete front matter proposal for the affected artifact, e.g.:
3248
+
3249
+ \`\`\`yaml
3250
+ code:
3251
+ - src/cli/**
3252
+ capabilities:
3253
+ - task-management
3254
+ decisions:
3255
+ - ADR-001
3256
+ \`\`\`
3257
+
3258
+ ## Instructions
3259
+
3260
+ 1. Work through the hints in \`.kaddo/graph-hints.md\` one artifact at a time.
3261
+ 2. Propose only relationships you can justify from existing knowledge \u2014 never invent paths,
3262
+ capabilities, ADRs or capsules.
3263
+ 3. Prefer narrow, accurate values (e.g. \`src/payments/**\`, not \`src/**\`).
3264
+ 4. Mark uncertain proposals explicitly and ask the human to confirm.
3265
+ 5. Tell the human to apply the front matter and re-run \`kaddo graph export\` to verify.
3266
+
3267
+ ## Constraints
3268
+
3269
+ - Do **not** modify files \u2014 propose front matter for the human to apply.
3270
+ - Do **not** invent relationships, paths or IDs.
3271
+ - Do **not** read the full source tree; rely on declared knowledge and the inventory.
3272
+ - Do **not** run Git or make commits.
3273
+
3274
+ ## Output Format
3275
+
3276
+ Per artifact: the artifact id, the proposed front matter block, and a one-line reason. End with a
3277
+ note to re-run \`kaddo graph export\`.
3278
+
3279
+ ## Where to Save the Result
3280
+
3281
+ Nothing is saved automatically. The human edits the affected artifact front matter (Work Items,
3282
+ ADRs) and re-runs \`kaddo graph export\`.
3283
+
3284
+ ## Quality Checklist
3285
+
3286
+ - Every proposal maps to a real artifact, path, capability, ADR or capsule.
3287
+ - Globs are narrow and accurate; uncertainty is marked.
3288
+ - No files were modified; no Git was run.
3289
+ - The human is asked to confirm and re-export the graph.
3290
+ `;
3210
3291
  var AGENT_PROMPTS = [
3211
3292
  { fileName: "capability-agent.md", content: CAPABILITY_AGENT },
3212
3293
  { fileName: "architecture-agent.md", content: ARCHITECTURE_AGENT },
@@ -3230,7 +3311,9 @@ var AGENT_PROMPTS = [
3230
3311
  // Ownership proposals (precise code: globs — VS-052)
3231
3312
  { fileName: "ownership-agent.md", content: OWNERSHIP_AGENT },
3232
3313
  // Knowledge Capsule refinement (external context — VS-054)
3233
- { fileName: "capsule-agent.md", content: CAPSULE_AGENT }
3314
+ { fileName: "capsule-agent.md", content: CAPSULE_AGENT },
3315
+ // Graph relationship quality (metadata hints → precise front matter — VS-056)
3316
+ { fileName: "graph-agent.md", content: GRAPH_AGENT }
3234
3317
  // Every official prompt ends with its responsibility boundaries + Agent Trace footer.
3235
3318
  ].map((p2) => ({ fileName: p2.fileName, content: withResponsibilityTrace(p2.fileName, p2.content) }));
3236
3319
 
@@ -3246,7 +3329,8 @@ var AGENT_GROUPS = {
3246
3329
  "standards-agent.md",
3247
3330
  "module-design-agent.md",
3248
3331
  "adr-agent.md",
3249
- "capsule-agent.md"
3332
+ "capsule-agent.md",
3333
+ "graph-agent.md"
3250
3334
  ],
3251
3335
  delivery: [
3252
3336
  "backlog-agent.md",
@@ -3391,7 +3475,8 @@ var agentReadme = {
3391
3475
  "- `standards-agent.md` \u2014 propose lightweight coding/docs/architecture standards.",
3392
3476
  "- `stack-agent.md` \u2014 document technologies and stack decisions.",
3393
3477
  "- `module-design-agent.md` \u2014 document the design of a mapped module.",
3394
- "- `capsule-agent.md` \u2014 refine a Knowledge Capsule for external sharing (no secrets/source)."
3478
+ "- `capsule-agent.md` \u2014 refine a Knowledge Capsule for external sharing (no secrets/source).",
3479
+ "- `graph-agent.md` \u2014 review `kaddo graph export` hints and propose precise relationship front matter."
3395
3480
  ].join("\n")
3396
3481
  };
3397
3482
  var agentFiles = AGENT_PROMPTS.map((a) => ({
@@ -4161,8 +4246,8 @@ async function runCreate(type, opts = {}) {
4161
4246
  answers[question.frontMatterField] = answer.trim();
4162
4247
  }
4163
4248
  const id = nextWorkItemId(dir);
4164
- const slug3 = slugify(title);
4165
- const fileName = `${id}-${slug3}.md`;
4249
+ const slug4 = slugify(title);
4250
+ const fileName = `${id}-${slug4}.md`;
4166
4251
  const filePath = join(dir, DRAFT_DIR, fileName);
4167
4252
  const frontMatter2 = buildFrontMatter(id, workItemType, level, title.trim(), answers);
4168
4253
  const body = buildBody(workItemType, level, title.trim(), answers, levelDef.qualityGate);
@@ -4202,8 +4287,8 @@ async function runCreateModule(dir, modType) {
4202
4287
  answers[q.frontMatterField] = answer.trim();
4203
4288
  }
4204
4289
  const id = nextWorkItemId(dir);
4205
- const slug3 = slugify(title);
4206
- const fileName = `${id}-${slug3}.md`;
4290
+ const slug4 = slugify(title);
4291
+ const fileName = `${id}-${slug4}.md`;
4207
4292
  const filePath = join(dir, DRAFT_DIR, fileName);
4208
4293
  const frontMatter2 = buildModuleFrontMatter(id, modType, title.trim(), answers);
4209
4294
  const body = buildModuleBody(modType, title.trim(), answers);
@@ -4335,8 +4420,8 @@ function buildRoadmapWorkItem(opts) {
4335
4420
  const { id, type, level, candidate } = opts;
4336
4421
  const answers = opts.answers ?? {};
4337
4422
  const title = candidate.title.trim();
4338
- const slug3 = slugify(title);
4339
- const fileName = `${id}-${slug3}.md`;
4423
+ const slug4 = slugify(title);
4424
+ const fileName = `${id}-${slug4}.md`;
4340
4425
  const qualityGate = getLevel(level).qualityGate;
4341
4426
  const frontMatter2 = buildRoadmapFrontMatter(id, type, level, title, candidate, answers);
4342
4427
  const body = buildRoadmapBody(type, level, title, candidate, answers, qualityGate);
@@ -4485,7 +4570,8 @@ function parseArtifact(filePath, raw) {
4485
4570
  initiative: String(data.initiative ?? data.source_initiative ?? ""),
4486
4571
  source: data.source ? String(data.source) : "",
4487
4572
  sourceId: String(data.source_id ?? ""),
4488
- decisions: Array.isArray(data.decisions) ? data.decisions.map(String).filter(Boolean) : []
4573
+ decisions: Array.isArray(data.decisions) ? data.decisions.map(String).filter(Boolean) : [],
4574
+ capsules: Array.isArray(data.capsules) ? data.capsules.map(String).filter(Boolean) : []
4489
4575
  };
4490
4576
  } catch {
4491
4577
  return null;
@@ -5697,6 +5783,11 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
5697
5783
  const capId = `capsule:${cap.id}`;
5698
5784
  addNode({ id: capId, type: "knowledge-capsule", label: cap.id, path: cap.path });
5699
5785
  addEdge(capId, projId, "provides_external_context");
5786
+ for (const wi of selectedWIs) {
5787
+ if (wi.capsules.includes(cap.id)) {
5788
+ addEdge(`wi:${wi.id || wi.title}`, capId, "uses_external_knowledge");
5789
+ }
5790
+ }
5700
5791
  }
5701
5792
  }
5702
5793
  return {
@@ -5710,18 +5801,6 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
5710
5801
  edges
5711
5802
  };
5712
5803
  }
5713
- var RELATIONSHIP_EDGES = /* @__PURE__ */ new Set([
5714
- "belongs_to",
5715
- "materialized_as",
5716
- "owns",
5717
- "implements",
5718
- "depends_on",
5719
- "governs",
5720
- "provides_external_context"
5721
- ]);
5722
- function graphIsSparse(graph) {
5723
- return !graph.edges.some((e) => RELATIONSHIP_EDGES.has(e.type));
5724
- }
5725
5804
  function serializeGraphJson(graph) {
5726
5805
  return JSON.stringify(graph, null, 2) + "\n";
5727
5806
  }
@@ -5776,8 +5855,237 @@ function loadGraphSummary(dir) {
5776
5855
  }
5777
5856
  }
5778
5857
 
5779
- // src/core/knowledge-discovery.ts
5858
+ // src/core/graph-hints.ts
5780
5859
  var KNOWLEDGE3 = "knowledge";
5860
+ function toPosix3(p2) {
5861
+ return p2.replace(/\\/g, "/");
5862
+ }
5863
+ function slug2(s) {
5864
+ return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
5865
+ }
5866
+ function isAdr2(a) {
5867
+ return toPosix3(a.filePath).includes("/tech/decisions/") && Boolean(a.type);
5868
+ }
5869
+ function capabilityHeadings(dir) {
5870
+ const p2 = join(dir, KNOWLEDGE3, "product", "capabilities.md");
5871
+ if (!exists(p2)) return [];
5872
+ return readFile(p2).split(/\r?\n/).map((l) => l.match(/^#{2,3}\s+(.+?)\s*$/)).filter((m) => Boolean(m)).map((m) => m[1].trim()).filter((h) => !/^(summary|resumen|overview|capabilities|capacidades)$/i.test(h));
5873
+ }
5874
+ function humanMissing(field) {
5875
+ switch (field) {
5876
+ case "code":
5877
+ return "code ownership";
5878
+ case "capabilities":
5879
+ return "linked capability";
5880
+ case "decisions":
5881
+ return "linked decision";
5882
+ case "source":
5883
+ return "roadmap link";
5884
+ default:
5885
+ return field;
5886
+ }
5887
+ }
5888
+ function buildGraphHints(dir, graph, now = /* @__PURE__ */ new Date()) {
5889
+ const artifacts = discoverKnowledge(dir);
5890
+ const workItems = artifacts.filter((a) => a.isWorkItem);
5891
+ const activeWIs = workItems.filter((a) => a.lifecycle && isActiveState(a.lifecycle));
5892
+ const adrs = artifacts.filter(isAdr2);
5893
+ const capsules = loadExternalRegistry(dir);
5894
+ const hints = [];
5895
+ const referencedCapabilitySlugs = new Set(
5896
+ workItems.flatMap((w) => w.capabilities.map((c) => slug2(c)))
5897
+ );
5898
+ const referencedCapsuleIds = new Set(workItems.flatMap((w) => w.capsules.map((c) => c)));
5899
+ let activeWithoutCode = 0;
5900
+ let activeWithoutCapabilities = 0;
5901
+ let wisWithoutSource = 0;
5902
+ for (const wi of activeWIs) {
5903
+ const id = wi.id || wi.title;
5904
+ const missing = [];
5905
+ const suggested = {};
5906
+ if (wi.codeGlobs.length === 0) {
5907
+ missing.push("code");
5908
+ suggested.code = ["src/<area>/**"];
5909
+ activeWithoutCode++;
5910
+ }
5911
+ if (wi.capabilities.length === 0) {
5912
+ missing.push("capabilities");
5913
+ suggested.capabilities = ["<capability>"];
5914
+ activeWithoutCapabilities++;
5915
+ }
5916
+ if (wi.decisions.length === 0) {
5917
+ missing.push("decisions");
5918
+ suggested.decisions = ["ADR-XXX"];
5919
+ }
5920
+ const hasSource = Boolean(wi.sourceId) || Boolean(wi.initiative);
5921
+ if (!hasSource) {
5922
+ missing.push("source");
5923
+ wisWithoutSource++;
5924
+ }
5925
+ if (missing.length === 0) continue;
5926
+ hints.push({
5927
+ artifact_id: id,
5928
+ artifact_type: "work-item",
5929
+ path: wi.relPath,
5930
+ severity: "info",
5931
+ missing,
5932
+ reason: "Active Work Item has limited graph relationships.",
5933
+ message: `${id} has no ${missing.map(humanMissing).join(", ")}.`,
5934
+ suggested_front_matter: Object.keys(suggested).length > 0 ? suggested : void 0
5935
+ });
5936
+ }
5937
+ let adrsWithoutCode = 0;
5938
+ for (const adr of adrs) {
5939
+ if (adr.codeGlobs.length > 0) continue;
5940
+ adrsWithoutCode++;
5941
+ const id = adr.id || adr.title;
5942
+ hints.push({
5943
+ artifact_id: id,
5944
+ artifact_type: "decision",
5945
+ path: adr.relPath,
5946
+ severity: "info",
5947
+ missing: ["code"],
5948
+ reason: "This ADR defines technical decisions but does not declare which paths it governs.",
5949
+ message: `${id} has no governed code paths.`,
5950
+ suggested_front_matter: { code: ["<path/to/code>"] }
5951
+ });
5952
+ }
5953
+ for (const cap of capabilityHeadings(dir)) {
5954
+ if (referencedCapabilitySlugs.has(slug2(cap))) continue;
5955
+ hints.push({
5956
+ artifact_id: cap,
5957
+ artifact_type: "capability",
5958
+ severity: "info",
5959
+ missing: ["work-item"],
5960
+ reason: "This capability is declared but no Work Item references it yet.",
5961
+ message: `Capability "${cap}" is not linked to any Work Item.`
5962
+ });
5963
+ }
5964
+ let capsulesWithoutWi = 0;
5965
+ for (const cap of capsules) {
5966
+ if (referencedCapsuleIds.has(cap.id)) continue;
5967
+ capsulesWithoutWi++;
5968
+ hints.push({
5969
+ artifact_id: cap.id,
5970
+ artifact_type: "knowledge-capsule",
5971
+ path: cap.path,
5972
+ severity: "info",
5973
+ missing: ["work-item"],
5974
+ reason: "This Knowledge Capsule is available but no Work Item declares `capsules:` for it.",
5975
+ message: `Knowledge Capsule "${cap.id}" is not linked to any Work Item.`
5976
+ });
5977
+ }
5978
+ const inEdge = /* @__PURE__ */ new Set();
5979
+ for (const e of graph.edges) {
5980
+ inEdge.add(e.from);
5981
+ inEdge.add(e.to);
5982
+ }
5983
+ const nodes = graph.nodes.length;
5984
+ const connected = graph.nodes.filter((n) => inEdge.has(n.id)).length;
5985
+ const relationshipEdges = graph.edges.filter((e) => e.type !== "informs").length;
5986
+ const metrics = {
5987
+ nodes_count: nodes,
5988
+ edges_count: graph.edges.length,
5989
+ connected_nodes_count: connected,
5990
+ isolated_nodes_count: nodes - connected,
5991
+ active_work_items_without_code: activeWithoutCode,
5992
+ active_work_items_without_capabilities: activeWithoutCapabilities,
5993
+ work_items_without_source: wisWithoutSource,
5994
+ adrs_without_code: adrsWithoutCode,
5995
+ capsules_without_related_work_items: capsulesWithoutWi
5996
+ };
5997
+ const quality = assessQuality(nodes, relationshipEdges, hints.length);
5998
+ return {
5999
+ generated_at: now.toISOString(),
6000
+ quality,
6001
+ summary: { nodes, edges: graph.edges.length, hints: hints.length },
6002
+ metrics,
6003
+ hints
6004
+ };
6005
+ }
6006
+ function assessQuality(nodes, relationshipEdges, hintCount) {
6007
+ if (nodes === 0 || relationshipEdges === 0) return "empty";
6008
+ if (relationshipEdges / nodes < 0.25) return "sparse";
6009
+ if (hintCount > 0) return "partial";
6010
+ return "good";
6011
+ }
6012
+ var QUALITY_NOTE = {
6013
+ good: "Most active artifacts have meaningful relationships.",
6014
+ partial: "Some active artifacts have missing relationship metadata.",
6015
+ sparse: "The graph has many nodes but few meaningful edges.",
6016
+ empty: "The graph has almost no relationships."
6017
+ };
6018
+ function yamlBlock(fm) {
6019
+ const lines = ["```yaml"];
6020
+ for (const [k, vals] of Object.entries(fm)) {
6021
+ lines.push(`${k}:`);
6022
+ for (const v of vals) lines.push(` - ${v}`);
6023
+ }
6024
+ lines.push("```");
6025
+ return lines;
6026
+ }
6027
+ function renderGraphHintsMarkdown(report) {
6028
+ const lines = [];
6029
+ lines.push("# Kaddo Graph Hints");
6030
+ lines.push("");
6031
+ lines.push("Generated by `kaddo graph export`. Suggestions only \u2014 Kaddo never edits your artifacts.");
6032
+ lines.push("");
6033
+ lines.push("## Summary");
6034
+ lines.push("");
6035
+ lines.push(`- Relationship quality: ${report.quality} \u2014 ${QUALITY_NOTE[report.quality]}`);
6036
+ lines.push(`- Nodes: ${report.summary.nodes}`);
6037
+ lines.push(`- Edges: ${report.summary.edges}`);
6038
+ lines.push(`- Hints: ${report.summary.hints}`);
6039
+ lines.push("");
6040
+ if (report.hints.length === 0) {
6041
+ lines.push("No hints \u2014 the declared relationships look healthy. \u{1F389}");
6042
+ lines.push("");
6043
+ return lines.join("\n");
6044
+ }
6045
+ lines.push("## Hints");
6046
+ lines.push("");
6047
+ for (const h of report.hints) {
6048
+ lines.push(`### ${h.artifact_id}${h.path ? ` \u2014 \`${h.path}\`` : ""}`);
6049
+ lines.push("");
6050
+ lines.push("Missing metadata:");
6051
+ lines.push("");
6052
+ for (const m of h.missing) lines.push(`- \`${m}\``);
6053
+ lines.push("");
6054
+ if (h.suggested_front_matter) {
6055
+ lines.push("Suggested front matter:");
6056
+ lines.push("");
6057
+ lines.push(...yamlBlock(h.suggested_front_matter));
6058
+ lines.push("");
6059
+ }
6060
+ lines.push(`Reason: ${h.reason}`);
6061
+ lines.push("");
6062
+ }
6063
+ lines.push("> Use the `graph-agent` to turn these hints into precise front matter \u2014 you confirm and apply.");
6064
+ lines.push("");
6065
+ return lines.join("\n");
6066
+ }
6067
+ function serializeGraphHintsJson(report) {
6068
+ return JSON.stringify(report, null, 2) + "\n";
6069
+ }
6070
+ function loadGraphHints(dir) {
6071
+ const p2 = join(dir, ".kaddo", "graph-hints.json");
6072
+ if (!exists(p2)) return null;
6073
+ try {
6074
+ const report = JSON.parse(readFile(p2));
6075
+ const hints = Array.isArray(report.hints) ? report.hints : [];
6076
+ return {
6077
+ quality: report.quality ?? "empty",
6078
+ totalHints: hints.length,
6079
+ activeWorkItemHints: hints.filter((h) => h.artifact_type === "work-item").length,
6080
+ messages: hints.map((h) => h.message).filter(Boolean)
6081
+ };
6082
+ } catch {
6083
+ return null;
6084
+ }
6085
+ }
6086
+
6087
+ // src/core/knowledge-discovery.ts
6088
+ var KNOWLEDGE4 = "knowledge";
5781
6089
  var CONSOLIDATED_TYPE = {
5782
6090
  Business: "business",
5783
6091
  Product: "product",
@@ -5819,10 +6127,10 @@ function layerForType(type) {
5819
6127
  }
5820
6128
  function layerFromPath(filePath) {
5821
6129
  const p2 = filePath.replace(/\\/g, "/");
5822
- if (p2.includes(`/${KNOWLEDGE3}/business/`)) return "Business";
5823
- if (p2.includes(`/${KNOWLEDGE3}/product/`)) return "Product";
5824
- if (p2.includes(`/${KNOWLEDGE3}/tech/`)) return "Tech";
5825
- if (p2.includes(`/${KNOWLEDGE3}/delivery/`)) return "Delivery";
6130
+ if (p2.includes(`/${KNOWLEDGE4}/business/`)) return "Business";
6131
+ if (p2.includes(`/${KNOWLEDGE4}/product/`)) return "Product";
6132
+ if (p2.includes(`/${KNOWLEDGE4}/tech/`)) return "Tech";
6133
+ if (p2.includes(`/${KNOWLEDGE4}/delivery/`)) return "Delivery";
5826
6134
  return null;
5827
6135
  }
5828
6136
  function basename(p2) {
@@ -5835,7 +6143,7 @@ function discoverLayers(dir) {
5835
6143
  Tech: blank(),
5836
6144
  Delivery: blank()
5837
6145
  };
5838
- const archDir = join(dir, KNOWLEDGE3);
6146
+ const archDir = join(dir, KNOWLEDGE4);
5839
6147
  const artifacts = exists(archDir) ? readArtifacts(archDir) : [];
5840
6148
  for (const a of artifacts) {
5841
6149
  const type = a.type;
@@ -5857,7 +6165,7 @@ function discoverLayers(dir) {
5857
6165
  }
5858
6166
  if (type === "adr" || type === "decision") slot.hasDecision = true;
5859
6167
  }
5860
- if (existsDirWithMd(join(dir, KNOWLEDGE3, "tech", "decisions"))) acc.Tech.structured = true;
6168
+ if (existsDirWithMd(join(dir, KNOWLEDGE4, "tech", "decisions"))) acc.Tech.structured = true;
5861
6169
  return ["Business", "Product", "Tech", "Delivery"].map((layer2) => ({
5862
6170
  layer: layer2,
5863
6171
  status: statusFor(layer2, acc[layer2]),
@@ -6107,6 +6415,7 @@ function buildProjectExplanation(dir) {
6107
6415
  duplicateWorkItems,
6108
6416
  externalCapsules: loadExternalCapsules(dir),
6109
6417
  graph: loadGraphSummary(dir),
6418
+ graphHints: loadGraphHints(dir),
6110
6419
  layers,
6111
6420
  roadmap,
6112
6421
  mappedModules,
@@ -6250,6 +6559,10 @@ function renderExplanationHuman(exp) {
6250
6559
  lines.push("## Knowledge Graph");
6251
6560
  lines.push(`- Nodes: ${exp.graph.nodes}`);
6252
6561
  lines.push(`- Edges: ${exp.graph.edges}`);
6562
+ if (exp.graphHints) {
6563
+ lines.push(`- Quality: ${exp.graphHints.quality}`);
6564
+ lines.push(`- Hints: ${exp.graphHints.totalHints}`);
6565
+ }
6253
6566
  if (exp.graph.generatedAt) lines.push(`- Last exported: ${exp.graph.generatedAt}`);
6254
6567
  lines.push("");
6255
6568
  }
@@ -6661,6 +6974,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
6661
6974
  deliveryMix,
6662
6975
  external: loadExternalCapsules(dir),
6663
6976
  graph: loadGraphSummary(dir),
6977
+ graphHints: loadGraphHints(dir),
6664
6978
  mappedModules,
6665
6979
  missing,
6666
6980
  // VS-052: the handoff is driven by the REAL phase, not project.state, so the pack never
@@ -6838,6 +7152,22 @@ function renderContextPack(pack) {
6838
7152
  );
6839
7153
  parts.push("Full graph: `.kaddo/graph.json` / `.kaddo/graph.mmd` (run `kaddo graph export` to refresh).\n");
6840
7154
  }
7155
+ if (pack.graphHints && pack.graphHints.totalHints > 0) {
7156
+ parts.push("## Graph Hints\n");
7157
+ parts.push(
7158
+ [
7159
+ `Graph relationship quality: ${pack.graphHints.quality}`,
7160
+ `Active hints: ${pack.graphHints.activeWorkItemHints}`,
7161
+ "Suggested agent: graph-agent"
7162
+ ].join("\n") + "\n"
7163
+ );
7164
+ const shown = pack.graphHints.messages.slice(0, 3);
7165
+ parts.push(shown.map((m) => `- ${m}`).join("\n") + "\n");
7166
+ if (pack.graphHints.totalHints > shown.length) {
7167
+ parts.push(`(+${pack.graphHints.totalHints - shown.length} more in \`.kaddo/graph-hints.md\`)
7168
+ `);
7169
+ }
7170
+ }
6841
7171
  parts.push("## Missing Context\n");
6842
7172
  if (missing.length > 0) {
6843
7173
  parts.push(missing.map((m) => `- ${m}`).join("\n") + "\n");
@@ -7167,6 +7497,15 @@ function runUnderstand() {
7167
7497
  }
7168
7498
  console.log(" \u2192 Review the relevant capsule before changing integration behavior with it.");
7169
7499
  }
7500
+ const graphHints = loadGraphHints(dir);
7501
+ if (assessment.phase === "Active Delivery" && graphHints && graphHints.activeWorkItemHints > 0) {
7502
+ console.log("");
7503
+ console.log(
7504
+ `Graph hints: ${graphHints.activeWorkItemHints} active Work Item(s) have limited graph relationships (quality: ${graphHints.quality}).`
7505
+ );
7506
+ console.log(" \u2192 Review graph hints before continuing with implementation.");
7507
+ console.log(" Suggested agent: graph-agent (see .kaddo/graph-hints.md)");
7508
+ }
7170
7509
  const active = activeWorkItems(dir);
7171
7510
  if (active.length > 0) {
7172
7511
  console.log("");
@@ -8279,7 +8618,7 @@ ${QUALITY}
8279
8618
  - [ ] Capabilities describe outcomes, not implementation.
8280
8619
  - [ ] Each capability cites evidence or is flagged as an assumption.
8281
8620
  `;
8282
- var KNOWLEDGE4 = `---
8621
+ var KNOWLEDGE5 = `---
8283
8622
  type: current-state
8284
8623
  updated_at: YYYY-MM-DD
8285
8624
  ---
@@ -9289,7 +9628,7 @@ var KADDO_TEMPLATES = [
9289
9628
  description: "What is true about the product right now.",
9290
9629
  whenToUse: "Created by `kaddo init`; keep it current as the product evolves.",
9291
9630
  relatedCommand: "kaddo init",
9292
- content: KNOWLEDGE4
9631
+ content: KNOWLEDGE5
9293
9632
  },
9294
9633
  // business / product (bootstrap — consolidated, minimal)
9295
9634
  {
@@ -9941,7 +10280,7 @@ async function runBootstrap(dir = cwd()) {
9941
10280
  }
9942
10281
 
9943
10282
  // src/commands/capsule.ts
9944
- function slug2(s) {
10283
+ function slug3(s) {
9945
10284
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
9946
10285
  }
9947
10286
  function runCapsuleExport() {
@@ -9949,7 +10288,7 @@ function runCapsuleExport() {
9949
10288
  const config = requireConfig(dir);
9950
10289
  intro2("kaddo capsule export");
9951
10290
  const capsule = buildCapsule(dir, config);
9952
- const name = slug2(config.project.name);
10291
+ const name = slug3(config.project.name);
9953
10292
  const mdPath = join(".kaddo", "exports", `${name}.capsule.md`);
9954
10293
  const jsonPath = join(".kaddo", "exports", `${name}.capsule.json`);
9955
10294
  writeFile(join(dir, mdPath), renderCapsuleMarkdown(capsule));
@@ -9995,6 +10334,7 @@ function runGraphExport(opts = {}) {
9995
10334
  const writeJson = format !== "mermaid";
9996
10335
  const writeMermaid = format !== "json";
9997
10336
  const graph = buildGraph(dir, config, { scope });
10337
+ const hints = buildGraphHints(dir, graph);
9998
10338
  const written = [];
9999
10339
  if (writeJson) {
10000
10340
  const rel = join(".kaddo", "graph.json");
@@ -10006,13 +10346,20 @@ function runGraphExport(opts = {}) {
10006
10346
  writeFile(join(dir, rel), renderGraphMermaid(graph));
10007
10347
  written.push(rel.replace(/\\/g, "/"));
10008
10348
  }
10009
- if (graphIsSparse(graph)) {
10010
- log2.warn("Knowledge graph exported with limited relationships.");
10011
- log2.info("Tip: add `code`, `capabilities`, `decisions` or `source_id` front matter to improve graph quality.");
10012
- } else {
10013
- log2.success("Knowledge graph exported.");
10014
- }
10349
+ writeFile(join(dir, ".kaddo", "graph-hints.md"), renderGraphHintsMarkdown(hints));
10350
+ writeFile(join(dir, ".kaddo", "graph-hints.json"), serializeGraphHintsJson(hints));
10351
+ written.push(".kaddo/graph-hints.md", ".kaddo/graph-hints.json");
10352
+ log2.success("Knowledge graph exported.");
10015
10353
  log2.info(`Scope: ${scope} \xB7 Nodes: ${graph.nodes.length} \xB7 Edges: ${graph.edges.length}`);
10354
+ log2.info(`Relationship quality: ${hints.quality}`);
10355
+ if (hints.hints.length > 0) {
10356
+ const shown = hints.hints.slice(0, 5);
10357
+ log2.warn(`${hints.hints.length} metadata hint(s) available:`);
10358
+ for (const h of shown) log2.info(`- ${h.message}`);
10359
+ if (hints.hints.length > shown.length) {
10360
+ log2.info(`\u2026and ${hints.hints.length - shown.length} more \u2014 see .kaddo/graph-hints.md`);
10361
+ }
10362
+ }
10016
10363
  for (const f of written) log2.info(`- ${f}`);
10017
10364
  printCommandFooter("graph export");
10018
10365
  outro2("Knowledge graph ready.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/cli",
3
- "version": "3.17.0",
3
+ "version": "3.19.0",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {