@kaddo/cli 3.75.0 → 3.76.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.
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>admin</title>
8
- <script type="module" crossorigin src="/assets/index-BpOMKXrf.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-BPt-9--k.css">
8
+ <script type="module" crossorigin src="/assets/index-BuxKrty5.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-BPQdYfAp.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -68,6 +68,7 @@ import {
68
68
  transitionWorkItem as coreTransitionWorkItem,
69
69
  getWorkItemCaptureDefinition as coreGetCaptureDefinition,
70
70
  buildRefinementHandoff as coreBuildRefinementHandoff,
71
+ getSystemMapProjection as coreGetSystemMapProjection,
71
72
  WorkItemWriteError,
72
73
  exists,
73
74
  join,
@@ -135,6 +136,9 @@ function mapWriteError(err) {
135
136
  function getCaptureDefinition() {
136
137
  return coreGetCaptureDefinition();
137
138
  }
139
+ function getSystemMap(dir) {
140
+ return coreGetSystemMapProjection(dir);
141
+ }
138
142
  function getRefinementHandoff(dir, workItemId) {
139
143
  assertValidWorkItemId(workItemId);
140
144
  try {
@@ -577,6 +581,43 @@ var WorkItemCreateWithAnswersSchema = z.object({
577
581
  type: z.string().min(1),
578
582
  answers: z.record(z.string(), z.string()).optional()
579
583
  });
584
+ var SystemMapNodeSchema = z.object({
585
+ id: z.string(),
586
+ type: z.string(),
587
+ label: z.string(),
588
+ status: z.string().optional(),
589
+ path: z.string().optional(),
590
+ workItemRef: z.string().optional(),
591
+ knowledgeRef: z.object({ id: z.string(), layer: z.string() }).optional(),
592
+ moduleId: z.string().optional()
593
+ });
594
+ var SystemMapRelationshipSchema = z.object({
595
+ id: z.string(),
596
+ source: z.string(),
597
+ target: z.string(),
598
+ type: z.string(),
599
+ label: z.string()
600
+ });
601
+ var SystemMapGroupSchema = z.object({
602
+ id: z.string(),
603
+ label: z.string(),
604
+ repositoryId: z.string(),
605
+ available: z.boolean()
606
+ });
607
+ var SystemMapProjectionSchema = z.object({
608
+ system: z.object({ name: z.string() }),
609
+ nodes: z.array(SystemMapNodeSchema),
610
+ relationships: z.array(SystemMapRelationshipSchema),
611
+ groups: z.array(SystemMapGroupSchema),
612
+ metadata: z.object({
613
+ projectName: z.string(),
614
+ structure: z.string(),
615
+ nodeCount: z.number(),
616
+ relationshipCount: z.number(),
617
+ coverage: z.enum(["good", "partial", "sparse", "empty"]),
618
+ available: z.boolean()
619
+ })
620
+ });
580
621
  var ErrorResponseSchema = z.object({
581
622
  error: z.object({
582
623
  code: z.string(),
@@ -737,6 +778,7 @@ async function createAdminServer(opts) {
737
778
  app.get("/api/v1/admin/readiness", coreRoute(getProjectReadiness));
738
779
  app.get("/api/v1/admin/route", coreRoute(getProjectRoute));
739
780
  app.get("/api/v1/admin/findings", coreRoute(getFindings));
781
+ app.get("/api/v1/admin/system", coreRoute(getSystemMap));
740
782
  app.get("/api/v1/admin/knowledge/inventory", coreRoute(getKnowledgeInventory));
741
783
  app.get("/api/v1/admin/knowledge/artifact/:artifactId", async (request) => {
742
784
  try {
@@ -891,6 +933,10 @@ export {
891
933
  RouteStepSchema,
892
934
  SQLiteAdminStorage,
893
935
  SessionManager,
936
+ SystemMapGroupSchema,
937
+ SystemMapNodeSchema,
938
+ SystemMapProjectionSchema,
939
+ SystemMapRelationshipSchema,
894
940
  ValidationResultSchema,
895
941
  WorkItemCreateSchema,
896
942
  WorkItemCreateWithAnswersSchema,
package/dist/core.js CHANGED
@@ -1625,6 +1625,150 @@ function loadExternalCapsules(dir) {
1625
1625
  }
1626
1626
 
1627
1627
  // src/core/graph.ts
1628
+ var KNOWLEDGE2 = "knowledge";
1629
+ var ACTIVE_STATUSES = ["draft", "ready", "in-progress", "blocked"];
1630
+ var ALL_STATUSES = ["draft", "ready", "in-progress", "blocked", "completed"];
1631
+ function scopeStatuses(scope) {
1632
+ return scope === "all" ? { included: ALL_STATUSES, excluded: ["archived"] } : { included: ACTIVE_STATUSES, excluded: ["completed", "archived"] };
1633
+ }
1634
+ function toPosix2(p2) {
1635
+ return p2.replace(/\\/g, "/");
1636
+ }
1637
+ function slug(s) {
1638
+ return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
1639
+ }
1640
+ function isAdr(a) {
1641
+ return toPosix2(a.filePath).includes("/tech/decisions/") && Boolean(a.type);
1642
+ }
1643
+ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
1644
+ const scope = opts.scope ?? "active";
1645
+ const nodes = /* @__PURE__ */ new Map();
1646
+ const edges = [];
1647
+ const edgeKeys = /* @__PURE__ */ new Set();
1648
+ const addNode = (node) => {
1649
+ if (!nodes.has(node.id)) nodes.set(node.id, node);
1650
+ };
1651
+ const addEdge = (from, to, type) => {
1652
+ const key = `${from}|${to}|${type}`;
1653
+ if (edgeKeys.has(key)) return;
1654
+ edgeKeys.add(key);
1655
+ edges.push({ from, to, type });
1656
+ };
1657
+ const layerDocs = [
1658
+ { id: "business:business", type: "business", label: "Business", files: ["business/business.md"] },
1659
+ { id: "product:product", type: "product", label: "Product", files: ["product/product.md", "product/capabilities.md"] },
1660
+ { id: "tech:tech", type: "tech", label: "Tech", files: ["tech/current-state.md", "tech/codebase.md"] },
1661
+ { id: "delivery:delivery", type: "delivery", label: "Delivery", files: ["delivery/roadmap.md"] }
1662
+ ];
1663
+ const presentLayers = [];
1664
+ for (const layer of layerDocs) {
1665
+ const path3 = layer.files.map((f) => `${KNOWLEDGE2}/${f}`).find((rel) => exists(join(dir, rel)));
1666
+ if (path3) {
1667
+ addNode({ id: layer.id, type: layer.type, label: layer.label, path: path3 });
1668
+ presentLayers.push(layer.id);
1669
+ }
1670
+ }
1671
+ for (let i = 0; i < presentLayers.length - 1; i++) {
1672
+ addEdge(presentLayers[i], presentLayers[i + 1], "informs");
1673
+ }
1674
+ const all = discoverKnowledge(dir);
1675
+ const workItems = all.filter((a) => a.isWorkItem);
1676
+ const { included, excluded } = scopeStatuses(scope);
1677
+ const includedSet = new Set(included);
1678
+ const activeWICount = workItems.filter((a) => a.lifecycle && isActiveState(a.lifecycle)).length;
1679
+ const selectedWIs = workItems.filter((a) => a.lifecycle && includedSet.has(a.lifecycle));
1680
+ for (const wi of selectedWIs) {
1681
+ const id = wi.id || wi.title;
1682
+ if (!id || !id.trim()) continue;
1683
+ const wiNodeId = `wi:${id}`;
1684
+ addNode({
1685
+ id: wiNodeId,
1686
+ type: "work-item",
1687
+ label: `${id} ${wi.title}`.trim(),
1688
+ path: wi.relPath,
1689
+ status: wi.lifecycle,
1690
+ knowledge_level: wi.knowledgeLevel || void 0
1691
+ });
1692
+ for (const glob of wi.codeGlobs) {
1693
+ if (!glob || !glob.trim()) continue;
1694
+ const codeId = `code:${glob}`;
1695
+ addNode({ id: codeId, type: "code-glob", label: glob });
1696
+ addEdge(wiNodeId, codeId, "owns");
1697
+ }
1698
+ for (const cap of wi.capabilities) {
1699
+ if (!cap || !cap.trim()) continue;
1700
+ const capId = `capability:${slug(cap) || cap}`;
1701
+ addNode({ id: capId, type: "capability", label: cap });
1702
+ addEdge(wiNodeId, capId, "implements");
1703
+ }
1704
+ for (const dec of wi.decisions) {
1705
+ if (!dec || !dec.trim()) continue;
1706
+ const adrId = `adr:${dec}`;
1707
+ addNode({ id: adrId, type: "decision", label: dec });
1708
+ addEdge(wiNodeId, adrId, "depends_on");
1709
+ }
1710
+ if (wi.initiative) {
1711
+ const initId = `initiative:${slug(wi.initiative) || wi.initiative}`;
1712
+ addNode({ id: initId, type: "initiative", label: wi.initiative });
1713
+ addEdge(wiNodeId, initId, "belongs_to");
1714
+ }
1715
+ if (wi.source === "roadmap" && wi.sourceId) {
1716
+ const candId = `candidate:${wi.sourceId}`;
1717
+ addNode({ id: candId, type: "roadmap-candidate", label: wi.sourceId });
1718
+ addEdge(candId, wiNodeId, "materialized_as");
1719
+ }
1720
+ }
1721
+ for (const adr of all.filter(isAdr)) {
1722
+ const adrLabel = adr.id || adr.title;
1723
+ if (!adrLabel || !adrLabel.trim()) continue;
1724
+ const adrId = `adr:${adrLabel}`;
1725
+ const referenced = nodes.has(adrId);
1726
+ if (scope === "all" || referenced) {
1727
+ nodes.set(adrId, {
1728
+ id: adrId,
1729
+ type: "decision",
1730
+ label: `${adr.id} ${adr.title}`.trim() || adrLabel,
1731
+ path: adr.relPath
1732
+ });
1733
+ for (const glob of adr.codeGlobs) {
1734
+ if (!glob || !glob.trim()) continue;
1735
+ const codeId = `code:${glob}`;
1736
+ addNode({ id: codeId, type: "code-glob", label: glob });
1737
+ addEdge(adrId, codeId, "governs");
1738
+ }
1739
+ }
1740
+ }
1741
+ const capsules = loadExternalRegistry(dir);
1742
+ if (capsules.length > 0) {
1743
+ const projId = `project:${slug(config.project.name) || "project"}`;
1744
+ addNode({ id: projId, type: "project", label: config.project.name });
1745
+ for (const cap of capsules) {
1746
+ const capId = `capsule:${cap.id}`;
1747
+ addNode({ id: capId, type: "knowledge-capsule", label: cap.id, path: cap.path });
1748
+ addEdge(capId, projId, "provides_external_context");
1749
+ for (const wi of selectedWIs) {
1750
+ if (wi.capsules.includes(cap.id)) {
1751
+ addEdge(`wi:${wi.id || wi.title}`, capId, "uses_external_knowledge");
1752
+ }
1753
+ }
1754
+ }
1755
+ }
1756
+ const scopeReason = scope === "all" ? "All supported Work Item statuses are included." : activeWICount === 0 ? "No active Work Items found. Completed Work Items are excluded from active scope." : "Active Work Items only; completed and archived are excluded.";
1757
+ return {
1758
+ generated_at: now.toISOString(),
1759
+ project: {
1760
+ name: config.project.name,
1761
+ state: config.project.state,
1762
+ structure: config.project.structure
1763
+ },
1764
+ scope,
1765
+ scope_reason: scopeReason,
1766
+ included_statuses: included,
1767
+ excluded_statuses: excluded,
1768
+ nodes: [...nodes.values()],
1769
+ edges
1770
+ };
1771
+ }
1628
1772
  var ACTIVE_WI_STATES = /* @__PURE__ */ new Set(["draft", "ready", "in-progress", "blocked"]);
1629
1773
  function loadGraphSummary(dir) {
1630
1774
  const p2 = join(dir, ".kaddo", "graph.json");
@@ -1656,6 +1800,161 @@ function loadGraphSummary(dir) {
1656
1800
  }
1657
1801
 
1658
1802
  // src/core/graph-hints.ts
1803
+ var KNOWLEDGE3 = "knowledge";
1804
+ function toPosix3(p2) {
1805
+ return p2.replace(/\\/g, "/");
1806
+ }
1807
+ function slug2(s) {
1808
+ return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
1809
+ }
1810
+ function isAdr2(a) {
1811
+ return toPosix3(a.filePath).includes("/tech/decisions/") && Boolean(a.type);
1812
+ }
1813
+ function capabilityHeadings(dir) {
1814
+ const p2 = join(dir, KNOWLEDGE3, "product", "capabilities.md");
1815
+ if (!exists(p2)) return [];
1816
+ 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));
1817
+ }
1818
+ function humanMissing(field) {
1819
+ switch (field) {
1820
+ case "code":
1821
+ return "code ownership";
1822
+ case "capabilities":
1823
+ return "linked capability";
1824
+ case "decisions":
1825
+ return "linked decision";
1826
+ case "source":
1827
+ return "roadmap link";
1828
+ default:
1829
+ return field;
1830
+ }
1831
+ }
1832
+ function buildGraphHints(dir, graph, now = /* @__PURE__ */ new Date()) {
1833
+ const artifacts = discoverKnowledge(dir);
1834
+ const workItems = artifacts.filter((a) => a.isWorkItem);
1835
+ const activeWIs = workItems.filter((a) => a.lifecycle && isActiveState(a.lifecycle));
1836
+ const adrs = artifacts.filter(isAdr2);
1837
+ const capsules = loadExternalRegistry(dir);
1838
+ const hints = [];
1839
+ const referencedCapabilitySlugs = new Set(
1840
+ workItems.flatMap((w) => w.capabilities.map((c) => slug2(c)))
1841
+ );
1842
+ const referencedCapsuleIds = new Set(workItems.flatMap((w) => w.capsules.map((c) => c)));
1843
+ let activeWithoutCode = 0;
1844
+ let activeWithoutCapabilities = 0;
1845
+ let wisWithoutSource = 0;
1846
+ for (const wi of activeWIs) {
1847
+ const id = wi.id || wi.title;
1848
+ const missing = [];
1849
+ const suggested = {};
1850
+ if (wi.codeGlobs.length === 0) {
1851
+ missing.push("code");
1852
+ suggested.code = ["src/<area>/**"];
1853
+ activeWithoutCode++;
1854
+ }
1855
+ if (wi.capabilities.length === 0) {
1856
+ missing.push("capabilities");
1857
+ suggested.capabilities = ["<capability>"];
1858
+ activeWithoutCapabilities++;
1859
+ }
1860
+ if (wi.decisions.length === 0) {
1861
+ missing.push("decisions");
1862
+ suggested.decisions = ["ADR-XXX"];
1863
+ }
1864
+ const hasSource = Boolean(wi.sourceId) || Boolean(wi.initiative);
1865
+ if (!hasSource) {
1866
+ missing.push("source");
1867
+ wisWithoutSource++;
1868
+ }
1869
+ if (missing.length === 0) continue;
1870
+ hints.push({
1871
+ artifact_id: id,
1872
+ artifact_type: "work-item",
1873
+ path: wi.relPath,
1874
+ severity: "info",
1875
+ missing,
1876
+ reason: "Active Work Item has limited graph relationships.",
1877
+ message: `${id} has no ${missing.map(humanMissing).join(", ")}.`,
1878
+ suggested_front_matter: Object.keys(suggested).length > 0 ? suggested : void 0
1879
+ });
1880
+ }
1881
+ let adrsWithoutCode = 0;
1882
+ for (const adr of adrs) {
1883
+ if (adr.codeGlobs.length > 0) continue;
1884
+ adrsWithoutCode++;
1885
+ const id = adr.id || adr.title;
1886
+ hints.push({
1887
+ artifact_id: id,
1888
+ artifact_type: "decision",
1889
+ path: adr.relPath,
1890
+ severity: "info",
1891
+ missing: ["code"],
1892
+ reason: "This ADR defines technical decisions but does not declare which paths it governs.",
1893
+ message: `${id} has no governed code paths.`,
1894
+ suggested_front_matter: { code: ["<path/to/code>"] }
1895
+ });
1896
+ }
1897
+ for (const cap of capabilityHeadings(dir)) {
1898
+ if (referencedCapabilitySlugs.has(slug2(cap))) continue;
1899
+ hints.push({
1900
+ artifact_id: cap,
1901
+ artifact_type: "capability",
1902
+ severity: "info",
1903
+ missing: ["work-item"],
1904
+ reason: "This capability is declared but no Work Item references it yet.",
1905
+ message: `Capability "${cap}" is not linked to any Work Item.`
1906
+ });
1907
+ }
1908
+ let capsulesWithoutWi = 0;
1909
+ for (const cap of capsules) {
1910
+ if (referencedCapsuleIds.has(cap.id)) continue;
1911
+ capsulesWithoutWi++;
1912
+ hints.push({
1913
+ artifact_id: cap.id,
1914
+ artifact_type: "knowledge-capsule",
1915
+ path: cap.path,
1916
+ severity: "info",
1917
+ missing: ["work-item"],
1918
+ reason: "This Knowledge Capsule is available but no Work Item declares `capsules:` for it.",
1919
+ message: `Knowledge Capsule "${cap.id}" is not linked to any Work Item.`
1920
+ });
1921
+ }
1922
+ const inEdge = /* @__PURE__ */ new Set();
1923
+ for (const e of graph.edges) {
1924
+ inEdge.add(e.from);
1925
+ inEdge.add(e.to);
1926
+ }
1927
+ const nodes = graph.nodes.length;
1928
+ const connected = graph.nodes.filter((n) => inEdge.has(n.id)).length;
1929
+ const relationshipEdges = graph.edges.filter((e) => e.type !== "informs").length;
1930
+ const metrics = {
1931
+ nodes_count: nodes,
1932
+ edges_count: graph.edges.length,
1933
+ connected_nodes_count: connected,
1934
+ isolated_nodes_count: nodes - connected,
1935
+ active_work_items_without_code: activeWithoutCode,
1936
+ active_work_items_without_capabilities: activeWithoutCapabilities,
1937
+ work_items_without_source: wisWithoutSource,
1938
+ adrs_without_code: adrsWithoutCode,
1939
+ capsules_without_related_work_items: capsulesWithoutWi
1940
+ };
1941
+ const quality = assessQuality(nodes, relationshipEdges, hints.length);
1942
+ return {
1943
+ generated_at: now.toISOString(),
1944
+ scope: graph.scope,
1945
+ scope_reason: graph.scope_reason,
1946
+ quality,
1947
+ summary: { nodes, edges: graph.edges.length, hints: hints.length },
1948
+ metrics,
1949
+ hints
1950
+ };
1951
+ }
1952
+ function assessQuality(nodes, relationshipEdges, hintCount) {
1953
+ if (nodes === 0 || relationshipEdges === 0) return "empty";
1954
+ if (relationshipEdges / nodes < 0.25) return "sparse";
1955
+ if (hintCount > 0) return "partial";
1956
+ return "good";
1957
+ }
1659
1958
  function loadGraphHints(dir) {
1660
1959
  const p2 = join(dir, ".kaddo", "graph-hints.json");
1661
1960
  if (!exists(p2)) return null;
@@ -1708,7 +2007,7 @@ function skillGroupCounts(skills) {
1708
2007
  }
1709
2008
 
1710
2009
  // src/core/knowledge-discovery.ts
1711
- var KNOWLEDGE2 = "knowledge";
2010
+ var KNOWLEDGE4 = "knowledge";
1712
2011
  var CONSOLIDATED_TYPE = {
1713
2012
  Business: "business",
1714
2013
  Product: "product",
@@ -1750,10 +2049,10 @@ function layerForType(type) {
1750
2049
  }
1751
2050
  function layerFromPath(filePath) {
1752
2051
  const p2 = filePath.replace(/\\/g, "/");
1753
- if (p2.includes(`/${KNOWLEDGE2}/business/`)) return "Business";
1754
- if (p2.includes(`/${KNOWLEDGE2}/product/`)) return "Product";
1755
- if (p2.includes(`/${KNOWLEDGE2}/tech/`)) return "Tech";
1756
- if (p2.includes(`/${KNOWLEDGE2}/delivery/`)) return "Delivery";
2052
+ if (p2.includes(`/${KNOWLEDGE4}/business/`)) return "Business";
2053
+ if (p2.includes(`/${KNOWLEDGE4}/product/`)) return "Product";
2054
+ if (p2.includes(`/${KNOWLEDGE4}/tech/`)) return "Tech";
2055
+ if (p2.includes(`/${KNOWLEDGE4}/delivery/`)) return "Delivery";
1757
2056
  return null;
1758
2057
  }
1759
2058
  function basename(p2) {
@@ -1766,7 +2065,7 @@ function discoverLayers(dir) {
1766
2065
  Tech: blank(),
1767
2066
  Delivery: blank()
1768
2067
  };
1769
- const archDir = join(dir, KNOWLEDGE2);
2068
+ const archDir = join(dir, KNOWLEDGE4);
1770
2069
  const artifacts = exists(archDir) ? readArtifacts(archDir) : [];
1771
2070
  for (const a of artifacts) {
1772
2071
  const type = a.type;
@@ -1788,7 +2087,7 @@ function discoverLayers(dir) {
1788
2087
  }
1789
2088
  if (type === "adr" || type === "decision") slot.hasDecision = true;
1790
2089
  }
1791
- if (existsDirWithMd(join(dir, KNOWLEDGE2, "tech", "decisions"))) acc.Tech.structured = true;
2090
+ if (existsDirWithMd(join(dir, KNOWLEDGE4, "tech", "decisions"))) acc.Tech.structured = true;
1792
2091
  return ["Business", "Product", "Tech", "Delivery"].map((layer) => ({
1793
2092
  layer,
1794
2093
  status: statusFor(layer, acc[layer]),
@@ -8412,6 +8711,89 @@ function buildRefinementHandoff(dir, workItemId) {
8412
8711
  text: lines.join("\n")
8413
8712
  };
8414
8713
  }
8714
+
8715
+ // src/core/system-map.ts
8716
+ var EDGE_LABELS = {
8717
+ informs: "informs",
8718
+ belongs_to: "belongs to",
8719
+ materialized_as: "materialized as",
8720
+ owns: "owns",
8721
+ implements: "implements",
8722
+ depends_on: "depends on",
8723
+ governs: "governs",
8724
+ provides_external_context: "provides external context",
8725
+ uses_external_knowledge: "uses external knowledge"
8726
+ };
8727
+ function emptyProjection(name, structure) {
8728
+ return {
8729
+ system: { name },
8730
+ nodes: [],
8731
+ relationships: [],
8732
+ groups: [],
8733
+ metadata: { projectName: name, structure, nodeCount: 0, relationshipCount: 0, coverage: "empty", available: false }
8734
+ };
8735
+ }
8736
+ function getSystemMapProjection(dir) {
8737
+ const config = loadConfig(dir);
8738
+ if (!config) return emptyProjection("unknown", "unknown");
8739
+ const graph = buildGraph(dir, config, { scope: "all" });
8740
+ const hints = buildGraphHints(dir, graph);
8741
+ const knowledgeByPath = /* @__PURE__ */ new Map();
8742
+ for (const a of discoverKnowledge(dir).filter((a2) => !a2.isWorkItem)) {
8743
+ const id = a.id || a.relPath.replace(/[/\\]/g, "-").replace(/\.md$/, "");
8744
+ const layer = a.layer === "module" ? "tech" : a.layer;
8745
+ knowledgeByPath.set(a.relPath, { id, layer });
8746
+ }
8747
+ const wiModules = /* @__PURE__ */ new Map();
8748
+ for (const wi of discoverWorkItems(dir)) wiModules.set(wi.id || wi.title, wi.affectedModules);
8749
+ const mapped = loadMappedModules(dir);
8750
+ const groupIds = /* @__PURE__ */ new Set(["core", ...mapped.map((m) => m.id)]);
8751
+ const nodes = graph.nodes.map((n) => toNode(n, knowledgeByPath, wiModules, groupIds));
8752
+ const relationships = graph.edges.map((e) => ({
8753
+ id: `${e.from}~${e.type}~${e.to}`,
8754
+ source: e.from,
8755
+ target: e.to,
8756
+ type: e.type,
8757
+ label: EDGE_LABELS[e.type] ?? e.type.replace(/_/g, " ")
8758
+ }));
8759
+ const usedGroups = new Set(nodes.map((n) => n.moduleId).filter(Boolean));
8760
+ const allGroups = [
8761
+ { id: "core", label: "core", repositoryId: "core", available: true },
8762
+ // A mapped module is available when its repository path resolves relative to the project.
8763
+ ...mapped.map((m) => ({ id: m.id, label: m.id, repositoryId: m.id, available: m.repoPath ? exists(join(dir, m.repoPath)) : false }))
8764
+ ];
8765
+ const groups = allGroups.filter((g) => usedGroups.has(g.id) || g.available === false);
8766
+ return {
8767
+ system: { name: config.project.name },
8768
+ nodes,
8769
+ relationships,
8770
+ groups,
8771
+ metadata: {
8772
+ projectName: config.project.name,
8773
+ structure: config.project.structure,
8774
+ nodeCount: nodes.length,
8775
+ relationshipCount: relationships.length,
8776
+ coverage: hints.quality,
8777
+ available: nodes.length > 0
8778
+ }
8779
+ };
8780
+ }
8781
+ function toNode(n, knowledgeByPath, wiModules, groupIds) {
8782
+ const node = { id: n.id, type: n.type, label: n.label };
8783
+ if (n.status) node.status = n.status;
8784
+ if (n.path && !/^([a-zA-Z]:[\\/]|\/)/.test(n.path)) node.path = n.path;
8785
+ if (n.type === "work-item") {
8786
+ const wiId = n.id.startsWith("wi:") ? n.id.slice(3) : n.id;
8787
+ node.workItemRef = wiId;
8788
+ const affected = (wiModules.get(wiId) ?? []).filter((m) => groupIds.has(m));
8789
+ if (affected.length === 1) node.moduleId = affected[0];
8790
+ }
8791
+ if (n.path) {
8792
+ const ref = knowledgeByPath.get(n.path);
8793
+ if (ref) node.knowledgeRef = ref;
8794
+ }
8795
+ return node;
8796
+ }
8415
8797
  export {
8416
8798
  WorkItemNotFoundError,
8417
8799
  WorkItemWriteError,
@@ -8427,6 +8809,7 @@ export {
8427
8809
  discoverKnowledge,
8428
8810
  discoverWorkItems,
8429
8811
  exists,
8812
+ getSystemMapProjection,
8430
8813
  getWorkItem,
8431
8814
  getWorkItemCaptureDefinition,
8432
8815
  getWorkItemForEdit,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/cli",
3
- "version": "3.75.0",
3
+ "version": "3.76.0",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,2 +0,0 @@
1
- /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.collapse{visibility:collapse}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.resize{resize:both}.border{border-style:var(--tw-border-style);border-width:1px}.font-mono{font-family:var(--font-mono)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}}:root{--background:#fff;--surface:#f8f9fa;--surface-muted:#f1f3f5;--foreground:#1a1a2e;--foreground-muted:#6c757d;--border:#dee2e6;--border-strong:#adb5bd;--primary:#4361ee;--primary-foreground:#fff;--success:#2d9f5c;--warning:#e9a820;--danger:#dc3545;--info:#3b82f6;--finding-blocking:#dc3545;--finding-warning:#e9a820;--finding-fyi:#6c757d;--work-item-draft:#6c757d;--work-item-ready:#3b82f6;--work-item-progress:#8b5cf6;--work-item-blocked:#dc3545;--work-item-completed:#2d9f5c;--work-item-archived:#adb5bd;--knowledge-ready:#2d9f5c;--knowledge-missing:#dc3545;--knowledge-placeholder:#e9a820;--knowledge-unknown:#6c757d;--module-core:#4361ee;--module-module:#3b82f6;--module-unavailable:#dc3545;--readiness-ready:#2d9f5c;--readiness-warning:#e9a820;--readiness-blocked:#dc3545;--font-sans:"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono:"JetBrains Mono", "Fira Code", "Cascadia Code", monospace;--radius:6px}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--background:#0f0f23;--surface:#1a1a2e;--surface-muted:#16213e;--foreground:#e8e8e8;--foreground-muted:#a0a0b0;--border:#2a2a3e;--border-strong:#4a4a5e;--primary:#6580f5;--primary-foreground:#fff;--success:#3cb371;--warning:#f0b840;--danger:#ef4444;--info:#60a5fa;--finding-blocking:#ef4444;--finding-warning:#f0b840;--finding-fyi:#a0a0b0;--work-item-draft:#a0a0b0;--work-item-ready:#60a5fa;--work-item-progress:#a78bfa;--work-item-blocked:#ef4444;--work-item-completed:#3cb371;--work-item-archived:#6a6a7e;--knowledge-ready:#3cb371;--knowledge-missing:#ef4444;--knowledge-placeholder:#f0b840;--knowledge-unknown:#a0a0b0;--module-core:#6580f5;--module-module:#60a5fa;--module-unavailable:#ef4444;--readiness-ready:#3cb371;--readiness-warning:#f0b840;--readiness-blocked:#ef4444}}:root[data-theme=dark]{--background:#0f0f23;--surface:#1a1a2e;--surface-muted:#16213e;--foreground:#e8e8e8;--foreground-muted:#a0a0b0;--border:#2a2a3e;--border-strong:#4a4a5e;--primary:#6580f5;--primary-foreground:#fff;--success:#3cb371;--warning:#f0b840;--danger:#ef4444;--info:#60a5fa;--finding-blocking:#ef4444;--finding-warning:#f0b840;--finding-fyi:#a0a0b0;--work-item-draft:#a0a0b0;--work-item-ready:#60a5fa;--work-item-progress:#a78bfa;--work-item-blocked:#ef4444;--work-item-completed:#3cb371;--work-item-archived:#6a6a7e;--knowledge-ready:#3cb371;--knowledge-missing:#ef4444;--knowledge-placeholder:#f0b840;--knowledge-unknown:#a0a0b0;--module-core:#6580f5;--module-module:#60a5fa;--module-unavailable:#ef4444;--readiness-ready:#3cb371;--readiness-warning:#f0b840;--readiness-blocked:#ef4444}body{font-family:var(--font-sans);background:var(--background);color:var(--foreground);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0}code,.font-mono{font-family:var(--font-mono)}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}