@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.
- package/dist/admin-dist/assets/index-BPQdYfAp.css +2 -0
- package/dist/admin-dist/assets/index-BuxKrty5.js +44 -0
- package/dist/admin-dist/index.html +2 -2
- package/dist/admin-server/index.js +46 -0
- package/dist/core.js +390 -7
- package/package.json +1 -1
- package/dist/admin-dist/assets/index-BPt-9--k.css +0 -2
- package/dist/admin-dist/assets/index-BpOMKXrf.js +0 -38
|
@@ -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-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
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
|
|
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(`/${
|
|
1754
|
-
if (p2.includes(`/${
|
|
1755
|
-
if (p2.includes(`/${
|
|
1756
|
-
if (p2.includes(`/${
|
|
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,
|
|
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,
|
|
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,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}
|