@kaddo/cli 3.9.0 → 3.10.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/index.js +319 -218
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4044,9 +4044,11 @@ function parseArtifact(filePath, raw) {
|
|
|
4044
4044
|
knowledgeLevel: String(data.knowledge_level ?? ""),
|
|
4045
4045
|
codeGlobs: globs,
|
|
4046
4046
|
domains: Array.isArray(data.domains) ? data.domains : [],
|
|
4047
|
+
capabilities: Array.isArray(data.capabilities) ? data.capabilities.map(String).filter(Boolean) : [],
|
|
4047
4048
|
status: String(data.status ?? ""),
|
|
4048
4049
|
phase: String(data.phase ?? ""),
|
|
4049
|
-
initiative: String(data.initiative ?? data.source_initiative ?? "")
|
|
4050
|
+
initiative: String(data.initiative ?? data.source_initiative ?? ""),
|
|
4051
|
+
source: data.source ? String(data.source) : ""
|
|
4050
4052
|
};
|
|
4051
4053
|
} catch {
|
|
4052
4054
|
return null;
|
|
@@ -4071,6 +4073,94 @@ function readArtifacts(archDir) {
|
|
|
4071
4073
|
return artifacts;
|
|
4072
4074
|
}
|
|
4073
4075
|
|
|
4076
|
+
// src/core/lifecycle.ts
|
|
4077
|
+
var LIFECYCLE_STATES = [
|
|
4078
|
+
"draft",
|
|
4079
|
+
"ready",
|
|
4080
|
+
"in-progress",
|
|
4081
|
+
"blocked",
|
|
4082
|
+
"completed",
|
|
4083
|
+
"archived"
|
|
4084
|
+
];
|
|
4085
|
+
var ACTIVE_STATES = ["draft", "ready", "in-progress", "blocked"];
|
|
4086
|
+
var LEGACY_STATUS_MAP = {
|
|
4087
|
+
done: "completed",
|
|
4088
|
+
cancelled: "archived",
|
|
4089
|
+
canceled: "archived",
|
|
4090
|
+
"in-progress": "in-progress"
|
|
4091
|
+
};
|
|
4092
|
+
var STATE_SET = new Set(LIFECYCLE_STATES);
|
|
4093
|
+
function isLifecycleState(s) {
|
|
4094
|
+
return STATE_SET.has(s);
|
|
4095
|
+
}
|
|
4096
|
+
function isActiveState(s) {
|
|
4097
|
+
return ACTIVE_STATES.includes(s);
|
|
4098
|
+
}
|
|
4099
|
+
function lifecycleFolderOf(filePath) {
|
|
4100
|
+
const p2 = filePath.replace(/\\/g, "/");
|
|
4101
|
+
const m = p2.match(/\/delivery\/work-items\/([^/]+)\//);
|
|
4102
|
+
if (m && isLifecycleState(m[1])) return m[1];
|
|
4103
|
+
return null;
|
|
4104
|
+
}
|
|
4105
|
+
function lifecycleStateOf(item) {
|
|
4106
|
+
const status = (item.status ?? "").trim().toLowerCase();
|
|
4107
|
+
if (isLifecycleState(status)) return status;
|
|
4108
|
+
if (status && LEGACY_STATUS_MAP[status]) return LEGACY_STATUS_MAP[status];
|
|
4109
|
+
const folder = lifecycleFolderOf(item.filePath ?? "");
|
|
4110
|
+
if (folder) return folder;
|
|
4111
|
+
return "ready";
|
|
4112
|
+
}
|
|
4113
|
+
function emptyLifecycleCounts() {
|
|
4114
|
+
return Object.fromEntries(LIFECYCLE_STATES.map((s) => [s, 0]));
|
|
4115
|
+
}
|
|
4116
|
+
function lifecycleCounts(states) {
|
|
4117
|
+
const counts = emptyLifecycleCounts();
|
|
4118
|
+
for (const s of states) counts[s]++;
|
|
4119
|
+
return counts;
|
|
4120
|
+
}
|
|
4121
|
+
|
|
4122
|
+
// src/services/knowledge-artifacts.ts
|
|
4123
|
+
var ARCH_DIR2 = "knowledge";
|
|
4124
|
+
function toPosix(p2) {
|
|
4125
|
+
return p2.replace(/\\/g, "/");
|
|
4126
|
+
}
|
|
4127
|
+
function relativeTo(dir, filePath) {
|
|
4128
|
+
const base = toPosix(dir).replace(/\/+$/, "");
|
|
4129
|
+
const full = toPosix(filePath);
|
|
4130
|
+
return full.startsWith(base + "/") ? full.slice(base.length + 1) : full;
|
|
4131
|
+
}
|
|
4132
|
+
function isWorkItemArtifact(a) {
|
|
4133
|
+
return toPosix(a.filePath).includes("/delivery/work-items/") && Boolean(a.type);
|
|
4134
|
+
}
|
|
4135
|
+
function classifyLayer(posixPath) {
|
|
4136
|
+
if (posixPath.includes("/knowledge/business/")) return "business";
|
|
4137
|
+
if (posixPath.includes("/knowledge/product/")) return "product";
|
|
4138
|
+
if (posixPath.includes("/knowledge/tech/modules/") || posixPath.includes("/knowledge/modules/"))
|
|
4139
|
+
return "module";
|
|
4140
|
+
if (posixPath.includes("/knowledge/tech/")) return "tech";
|
|
4141
|
+
if (posixPath.includes("/knowledge/delivery/")) return "delivery";
|
|
4142
|
+
return "unknown";
|
|
4143
|
+
}
|
|
4144
|
+
function enrich(dir, a) {
|
|
4145
|
+
const posix = toPosix(a.filePath);
|
|
4146
|
+
const isWi = isWorkItemArtifact(a);
|
|
4147
|
+
return {
|
|
4148
|
+
...a,
|
|
4149
|
+
relPath: relativeTo(dir, a.filePath),
|
|
4150
|
+
layer: classifyLayer(posix),
|
|
4151
|
+
isWorkItem: isWi,
|
|
4152
|
+
lifecycle: isWi ? lifecycleStateOf({ status: a.status, filePath: a.filePath }) : void 0
|
|
4153
|
+
};
|
|
4154
|
+
}
|
|
4155
|
+
function discoverKnowledge(dir) {
|
|
4156
|
+
const archDir = join(dir, ARCH_DIR2);
|
|
4157
|
+
if (!exists(archDir)) return [];
|
|
4158
|
+
return readArtifacts(archDir).map((a) => enrich(dir, a));
|
|
4159
|
+
}
|
|
4160
|
+
function discoverWorkItems(dir) {
|
|
4161
|
+
return discoverKnowledge(dir).filter((a) => a.isWorkItem);
|
|
4162
|
+
}
|
|
4163
|
+
|
|
4074
4164
|
// src/core/diff-analysis.ts
|
|
4075
4165
|
import { minimatch } from "minimatch";
|
|
4076
4166
|
function normalizePath(p2) {
|
|
@@ -4463,7 +4553,7 @@ function collectMatchedDomains(matchedArtifactDomains) {
|
|
|
4463
4553
|
}
|
|
4464
4554
|
|
|
4465
4555
|
// src/commands/guard.ts
|
|
4466
|
-
var
|
|
4556
|
+
var ARCH_DIR3 = "knowledge";
|
|
4467
4557
|
var CONFIG_PATH2 = ".kaddo/config.yml";
|
|
4468
4558
|
function loadConfig2(dir) {
|
|
4469
4559
|
const configPath = join(dir, CONFIG_PATH2);
|
|
@@ -4607,12 +4697,12 @@ async function runGuard(opts = {}) {
|
|
|
4607
4697
|
console.log("kaddo guard: no modified files detected.");
|
|
4608
4698
|
return;
|
|
4609
4699
|
}
|
|
4610
|
-
const archDir = join(dir,
|
|
4700
|
+
const archDir = join(dir, ARCH_DIR3);
|
|
4611
4701
|
if (!exists(archDir)) {
|
|
4612
4702
|
console.log("kaddo guard: no knowledge/ directory found. Run `kaddo init` first.");
|
|
4613
4703
|
return;
|
|
4614
4704
|
}
|
|
4615
|
-
const artifacts =
|
|
4705
|
+
const artifacts = discoverKnowledge(dir);
|
|
4616
4706
|
const result = analyzeGuard(touchedFiles, artifacts, silentWithoutOwnership);
|
|
4617
4707
|
const pluginSignals = runPlugins(plugins, currentFiles, (filePath) => {
|
|
4618
4708
|
const abs = join(dir, filePath);
|
|
@@ -4724,6 +4814,110 @@ function runIgnoreRemove(artifactId) {
|
|
|
4724
4814
|
import matter2 from "gray-matter";
|
|
4725
4815
|
import { parse as parseYaml8 } from "yaml";
|
|
4726
4816
|
|
|
4817
|
+
// src/core/delivery-phase.ts
|
|
4818
|
+
function layer(layers, name) {
|
|
4819
|
+
return layers.find((l) => l.layer === name)?.status ?? "Missing";
|
|
4820
|
+
}
|
|
4821
|
+
function baseComplete(layers) {
|
|
4822
|
+
return layer(layers, "Business") !== "Missing" && layer(layers, "Product") !== "Missing" && layer(layers, "Tech") !== "Missing";
|
|
4823
|
+
}
|
|
4824
|
+
function determinePhase(input) {
|
|
4825
|
+
const { roadmap, workItems } = input;
|
|
4826
|
+
const active = workItems.byState.draft + workItems.byState.ready + workItems.byState["in-progress"] + workItems.byState.blocked;
|
|
4827
|
+
if (!baseComplete(input.layers)) return "Discovery";
|
|
4828
|
+
if (!roadmap.present) return "Planning";
|
|
4829
|
+
if (workItems.total === 0) return "Delivery Preparation";
|
|
4830
|
+
if (active > 0) return "Active Delivery";
|
|
4831
|
+
return "Maintenance";
|
|
4832
|
+
}
|
|
4833
|
+
function ownershipPct(o) {
|
|
4834
|
+
if (o.workItemsTotal === 0) return 100;
|
|
4835
|
+
return Math.round(o.workItemsWithOwnership / o.workItemsTotal * 100);
|
|
4836
|
+
}
|
|
4837
|
+
function buildReasons(input) {
|
|
4838
|
+
const reasons = [];
|
|
4839
|
+
reasons.push(input.roadmap.present ? "Roadmap available" : "No roadmap yet");
|
|
4840
|
+
if (input.roadmap.present) {
|
|
4841
|
+
reasons.push(`${input.roadmap.materialized} materialized work item(s)`);
|
|
4842
|
+
if (input.roadmap.remaining > 0) reasons.push(`${input.roadmap.remaining} roadmap candidate(s) remaining`);
|
|
4843
|
+
}
|
|
4844
|
+
const active = LIFECYCLE_STATES.filter((s) => input.workItems.byState[s] > 0 && s !== "completed" && s !== "archived");
|
|
4845
|
+
if (active.length > 0) {
|
|
4846
|
+
reasons.push(active.map((s) => `${s}: ${input.workItems.byState[s]}`).join(", "));
|
|
4847
|
+
}
|
|
4848
|
+
if (input.workItems.total > 0) reasons.push(`Ownership coverage ${ownershipPct(input.ownership)}%`);
|
|
4849
|
+
return reasons;
|
|
4850
|
+
}
|
|
4851
|
+
function firstMissingLayerAgent(layers) {
|
|
4852
|
+
if (layer(layers, "Business") === "Missing")
|
|
4853
|
+
return { agent: "business-agent", step: "Use business-agent to create knowledge/business/business.md" };
|
|
4854
|
+
if (layer(layers, "Product") === "Missing")
|
|
4855
|
+
return { agent: "capability-agent", step: "Use capability-agent to create knowledge/product/capabilities.md" };
|
|
4856
|
+
return { agent: "architecture-agent", step: "Use architecture-agent to create knowledge/tech/current-state.md" };
|
|
4857
|
+
}
|
|
4858
|
+
function assessPhase(input) {
|
|
4859
|
+
const phase = determinePhase(input);
|
|
4860
|
+
const reasons = buildReasons(input);
|
|
4861
|
+
const bs = input.workItems.byState;
|
|
4862
|
+
const items = input.workItems.items;
|
|
4863
|
+
const firstOf = (s) => items.find((i) => i.lifecycle === s);
|
|
4864
|
+
let recommendedAgents2 = [];
|
|
4865
|
+
let nextStep = "";
|
|
4866
|
+
switch (phase) {
|
|
4867
|
+
case "Discovery": {
|
|
4868
|
+
const m = firstMissingLayerAgent(input.layers);
|
|
4869
|
+
recommendedAgents2 = [m.agent];
|
|
4870
|
+
nextStep = m.step;
|
|
4871
|
+
break;
|
|
4872
|
+
}
|
|
4873
|
+
case "Planning": {
|
|
4874
|
+
recommendedAgents2 = ["roadmap-agent"];
|
|
4875
|
+
nextStep = "Use roadmap-agent to create knowledge/delivery/roadmap.md";
|
|
4876
|
+
break;
|
|
4877
|
+
}
|
|
4878
|
+
case "Delivery Preparation": {
|
|
4879
|
+
recommendedAgents2 = ["kaddo create --from roadmap", "work-item-agent"];
|
|
4880
|
+
const n = input.roadmap.remaining || input.roadmap.candidates;
|
|
4881
|
+
nextStep = `Run \`kaddo create --from roadmap\`${n ? ` (${n} candidate(s))` : ""}, then refine with work-item-agent`;
|
|
4882
|
+
break;
|
|
4883
|
+
}
|
|
4884
|
+
case "Active Delivery": {
|
|
4885
|
+
if (bs.ready > 0) {
|
|
4886
|
+
const wi = firstOf("ready");
|
|
4887
|
+
recommendedAgents2 = ["implementation-agent"];
|
|
4888
|
+
nextStep = wi ? `Start ${wi.id} \u2014 ${wi.title} (ready \u2192 in-progress)` : "Start a ready Work Item";
|
|
4889
|
+
} else if (bs["in-progress"] > 0) {
|
|
4890
|
+
const wi = firstOf("in-progress");
|
|
4891
|
+
recommendedAgents2 = ["implementation-agent", "kaddo scan", "kaddo owners suggest", "kaddo guard"];
|
|
4892
|
+
nextStep = wi ? `Continue ${wi.id} \u2014 ${wi.title}; then run kaddo scan, owners suggest, guard` : "Continue the in-progress Work Item; then scan, owners suggest, guard";
|
|
4893
|
+
} else if (bs.draft > 0) {
|
|
4894
|
+
const wi = firstOf("draft");
|
|
4895
|
+
recommendedAgents2 = ["work-item-agent"];
|
|
4896
|
+
nextStep = wi ? `Refine ${wi.id} from draft to ready` : "Refine a draft Work Item to ready";
|
|
4897
|
+
} else if (bs.blocked > 0) {
|
|
4898
|
+
const wi = firstOf("blocked");
|
|
4899
|
+
recommendedAgents2 = ["work-item-agent"];
|
|
4900
|
+
nextStep = wi ? `Resolve the blocker on ${wi.id} \u2014 ${wi.title}` : "Resolve the blockers on active work";
|
|
4901
|
+
}
|
|
4902
|
+
if (input.ownership.workItemsMissingOwnership > 0) {
|
|
4903
|
+
recommendedAgents2.push("kaddo owners suggest");
|
|
4904
|
+
}
|
|
4905
|
+
break;
|
|
4906
|
+
}
|
|
4907
|
+
case "Maintenance": {
|
|
4908
|
+
if (input.roadmap.remaining > 0) {
|
|
4909
|
+
recommendedAgents2 = ["kaddo create --from roadmap", "work-item-agent"];
|
|
4910
|
+
nextStep = `Materialize ${input.roadmap.remaining} remaining roadmap candidate(s)`;
|
|
4911
|
+
} else {
|
|
4912
|
+
recommendedAgents2 = ["roadmap-agent"];
|
|
4913
|
+
nextStep = "No active work \u2014 use roadmap-agent to plan the next initiative";
|
|
4914
|
+
}
|
|
4915
|
+
break;
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
return { phase, reasons, recommendedAgents: recommendedAgents2, nextStep };
|
|
4919
|
+
}
|
|
4920
|
+
|
|
4727
4921
|
// src/core/knowledge-discovery.ts
|
|
4728
4922
|
var KNOWLEDGE = "knowledge";
|
|
4729
4923
|
var CONSOLIDATED_TYPE = {
|
|
@@ -4787,29 +4981,29 @@ function discoverLayers(dir) {
|
|
|
4787
4981
|
const artifacts = exists(archDir) ? readArtifacts(archDir) : [];
|
|
4788
4982
|
for (const a of artifacts) {
|
|
4789
4983
|
const type = a.type;
|
|
4790
|
-
const
|
|
4791
|
-
if (!
|
|
4792
|
-
const slot = acc[
|
|
4984
|
+
const layer2 = layerForType(type) ?? layerFromPath(a.filePath);
|
|
4985
|
+
if (!layer2) continue;
|
|
4986
|
+
const slot = acc[layer2];
|
|
4793
4987
|
slot.detected.add(basename(a.filePath));
|
|
4794
|
-
if (
|
|
4988
|
+
if (layer2 === "Delivery") {
|
|
4795
4989
|
if (type === "roadmap" || basename(a.filePath) === "roadmap.md") slot.hasRoadmap = true;
|
|
4796
4990
|
if (WORK_ITEM_TYPES2.has(type) || a.filePath.replace(/\\/g, "/").includes("/delivery/work-items/")) {
|
|
4797
4991
|
if (a.filePath.replace(/\\/g, "/").includes("/delivery/work-items/")) slot.hasWorkItem = true;
|
|
4798
4992
|
}
|
|
4799
4993
|
continue;
|
|
4800
4994
|
}
|
|
4801
|
-
if (type === CONSOLIDATED_TYPE[
|
|
4802
|
-
else if (STRUCTURED_TYPES[
|
|
4995
|
+
if (type === CONSOLIDATED_TYPE[layer2]) slot.consolidated = true;
|
|
4996
|
+
else if (STRUCTURED_TYPES[layer2].has(type)) slot.structured = true;
|
|
4803
4997
|
else if (!type) {
|
|
4804
4998
|
slot.consolidated = true;
|
|
4805
4999
|
}
|
|
4806
5000
|
if (type === "adr" || type === "decision") slot.hasDecision = true;
|
|
4807
5001
|
}
|
|
4808
5002
|
if (existsDirWithMd(join(dir, KNOWLEDGE, "tech", "decisions"))) acc.Tech.structured = true;
|
|
4809
|
-
return ["Business", "Product", "Tech", "Delivery"].map((
|
|
4810
|
-
layer,
|
|
4811
|
-
status: statusFor(
|
|
4812
|
-
detected: [...acc[
|
|
5003
|
+
return ["Business", "Product", "Tech", "Delivery"].map((layer2) => ({
|
|
5004
|
+
layer: layer2,
|
|
5005
|
+
status: statusFor(layer2, acc[layer2]),
|
|
5006
|
+
detected: [...acc[layer2].detected].sort()
|
|
4813
5007
|
}));
|
|
4814
5008
|
}
|
|
4815
5009
|
function blank() {
|
|
@@ -4830,8 +5024,8 @@ function existsDirWithMd(d) {
|
|
|
4830
5024
|
return false;
|
|
4831
5025
|
}
|
|
4832
5026
|
}
|
|
4833
|
-
function statusFor(
|
|
4834
|
-
if (
|
|
5027
|
+
function statusFor(layer2, a) {
|
|
5028
|
+
if (layer2 === "Delivery") {
|
|
4835
5029
|
if (a.hasWorkItem) return "Traceable";
|
|
4836
5030
|
if (a.hasRoadmap) return "Partial";
|
|
4837
5031
|
return "Missing";
|
|
@@ -4840,40 +5034,15 @@ function statusFor(layer, a) {
|
|
|
4840
5034
|
if (a.consolidated) return "Consolidated";
|
|
4841
5035
|
return "Missing";
|
|
4842
5036
|
}
|
|
4843
|
-
function roadmapHasUnmaterializedCandidates(dir) {
|
|
4844
|
-
const roadmap = join(dir, KNOWLEDGE, "delivery", "roadmap.md");
|
|
4845
|
-
if (!exists(roadmap)) return false;
|
|
4846
|
-
let hasCandidates = false;
|
|
4847
|
-
try {
|
|
4848
|
-
hasCandidates = /WI-[A-Z0-9-]*\d/i.test(readFile(roadmap));
|
|
4849
|
-
} catch {
|
|
4850
|
-
return false;
|
|
4851
|
-
}
|
|
4852
|
-
if (!hasCandidates) return false;
|
|
4853
|
-
const wiDir = join(dir, KNOWLEDGE, "delivery", "work-items");
|
|
4854
|
-
return !existsDirWithMd(wiDir);
|
|
4855
|
-
}
|
|
4856
5037
|
|
|
4857
5038
|
// src/core/layers.ts
|
|
4858
5039
|
function knowledgeLayers(dir) {
|
|
4859
5040
|
return discoverLayers(dir);
|
|
4860
5041
|
}
|
|
4861
|
-
var COMPLETE = {
|
|
4862
|
-
Business: (s) => s !== "Missing",
|
|
4863
|
-
Product: (s) => s !== "Missing",
|
|
4864
|
-
Tech: (s) => s !== "Missing",
|
|
4865
|
-
Delivery: (s) => s === "Traceable"
|
|
4866
|
-
};
|
|
4867
|
-
function currentPhase(layers) {
|
|
4868
|
-
for (const l of layers) {
|
|
4869
|
-
if (!COMPLETE[l.layer](l.status)) return l.layer;
|
|
4870
|
-
}
|
|
4871
|
-
return "Delivery";
|
|
4872
|
-
}
|
|
4873
5042
|
function renderLayersMarkdown(layers) {
|
|
4874
5043
|
const lines = [];
|
|
4875
|
-
for (const { layer, status, detected } of layers) {
|
|
4876
|
-
lines.push(`### ${
|
|
5044
|
+
for (const { layer: layer2, status, detected } of layers) {
|
|
5045
|
+
lines.push(`### ${layer2} \u2014 ${status}`);
|
|
4877
5046
|
if (detected.length > 0) for (const d of detected) lines.push(`- \u2713 ${d}`);
|
|
4878
5047
|
else lines.push("- \u2717 none");
|
|
4879
5048
|
lines.push("");
|
|
@@ -4881,54 +5050,8 @@ function renderLayersMarkdown(layers) {
|
|
|
4881
5050
|
return lines.join("\n").trimEnd();
|
|
4882
5051
|
}
|
|
4883
5052
|
|
|
4884
|
-
// src/core/lifecycle.ts
|
|
4885
|
-
var LIFECYCLE_STATES = [
|
|
4886
|
-
"draft",
|
|
4887
|
-
"ready",
|
|
4888
|
-
"in-progress",
|
|
4889
|
-
"blocked",
|
|
4890
|
-
"completed",
|
|
4891
|
-
"archived"
|
|
4892
|
-
];
|
|
4893
|
-
var ACTIVE_STATES = ["draft", "ready", "in-progress", "blocked"];
|
|
4894
|
-
var LEGACY_STATUS_MAP = {
|
|
4895
|
-
done: "completed",
|
|
4896
|
-
cancelled: "archived",
|
|
4897
|
-
canceled: "archived",
|
|
4898
|
-
"in-progress": "in-progress"
|
|
4899
|
-
};
|
|
4900
|
-
var STATE_SET = new Set(LIFECYCLE_STATES);
|
|
4901
|
-
function isLifecycleState(s) {
|
|
4902
|
-
return STATE_SET.has(s);
|
|
4903
|
-
}
|
|
4904
|
-
function isActiveState(s) {
|
|
4905
|
-
return ACTIVE_STATES.includes(s);
|
|
4906
|
-
}
|
|
4907
|
-
function lifecycleFolderOf(filePath) {
|
|
4908
|
-
const p2 = filePath.replace(/\\/g, "/");
|
|
4909
|
-
const m = p2.match(/\/delivery\/work-items\/([^/]+)\//);
|
|
4910
|
-
if (m && isLifecycleState(m[1])) return m[1];
|
|
4911
|
-
return null;
|
|
4912
|
-
}
|
|
4913
|
-
function lifecycleStateOf(item) {
|
|
4914
|
-
const status = (item.status ?? "").trim().toLowerCase();
|
|
4915
|
-
if (isLifecycleState(status)) return status;
|
|
4916
|
-
if (status && LEGACY_STATUS_MAP[status]) return LEGACY_STATUS_MAP[status];
|
|
4917
|
-
const folder = lifecycleFolderOf(item.filePath ?? "");
|
|
4918
|
-
if (folder) return folder;
|
|
4919
|
-
return "ready";
|
|
4920
|
-
}
|
|
4921
|
-
function emptyLifecycleCounts() {
|
|
4922
|
-
return Object.fromEntries(LIFECYCLE_STATES.map((s) => [s, 0]));
|
|
4923
|
-
}
|
|
4924
|
-
function lifecycleCounts(states) {
|
|
4925
|
-
const counts = emptyLifecycleCounts();
|
|
4926
|
-
for (const s of states) counts[s]++;
|
|
4927
|
-
return counts;
|
|
4928
|
-
}
|
|
4929
|
-
|
|
4930
5053
|
// src/core/project-explain.ts
|
|
4931
|
-
var
|
|
5054
|
+
var ARCH_DIR4 = "knowledge";
|
|
4932
5055
|
function first(values) {
|
|
4933
5056
|
if (Array.isArray(values)) {
|
|
4934
5057
|
const v = values.find((x) => typeof x === "string" && x);
|
|
@@ -4959,7 +5082,7 @@ function loadScan(dir) {
|
|
|
4959
5082
|
}
|
|
4960
5083
|
}
|
|
4961
5084
|
function hasAgents(dir) {
|
|
4962
|
-
const agentsDir = join(dir,
|
|
5085
|
+
const agentsDir = join(dir, ARCH_DIR4, "agents");
|
|
4963
5086
|
if (!exists(agentsDir)) return false;
|
|
4964
5087
|
function hasAgentMd(d) {
|
|
4965
5088
|
for (const e of readDir(d)) {
|
|
@@ -4996,7 +5119,7 @@ function buildProjectExplanation(dir) {
|
|
|
4996
5119
|
const layerStatus = (name) => layers.find((l) => l.layer === name)?.status ?? "Missing";
|
|
4997
5120
|
const knowledge = {
|
|
4998
5121
|
hasScan: scan2 !== null,
|
|
4999
|
-
hasInventory: exists(join(dir,
|
|
5122
|
+
hasInventory: exists(join(dir, ARCH_DIR4, "inventory.md")),
|
|
5000
5123
|
hasContextPack: exists(join(dir, ".kaddo", "context-pack.md")),
|
|
5001
5124
|
hasUnderstand: exists(join(dir, ".kaddo", "understand.md")),
|
|
5002
5125
|
hasCapabilities: layerStatus("Product") !== "Missing",
|
|
@@ -5004,12 +5127,7 @@ function buildProjectExplanation(dir) {
|
|
|
5004
5127
|
hasRoadmap: layerStatus("Delivery") !== "Missing",
|
|
5005
5128
|
hasAgents: hasAgents(dir)
|
|
5006
5129
|
};
|
|
5007
|
-
const
|
|
5008
|
-
const allArtifacts = exists(archDir) ? readArtifacts(archDir) : [];
|
|
5009
|
-
const workItemArtifacts = allArtifacts.filter((a) => {
|
|
5010
|
-
const p2 = a.filePath.replace(/\\/g, "/");
|
|
5011
|
-
return p2.includes("/delivery/work-items/") && Boolean(a.type);
|
|
5012
|
-
});
|
|
5130
|
+
const workItemArtifacts = discoverWorkItems(dir);
|
|
5013
5131
|
const items = workItemArtifacts.map((a) => ({
|
|
5014
5132
|
id: a.id || a.title,
|
|
5015
5133
|
title: a.title,
|
|
@@ -5051,7 +5169,7 @@ function buildProjectExplanation(dir) {
|
|
|
5051
5169
|
workItemsMissingOwnership: items.length - withOwnership
|
|
5052
5170
|
};
|
|
5053
5171
|
const domains = [...new Set(workItemArtifacts.flatMap((a) => a.domains))].filter(Boolean);
|
|
5054
|
-
const roadmapPath = join(dir,
|
|
5172
|
+
const roadmapPath = join(dir, ARCH_DIR4, "delivery", "roadmap.md");
|
|
5055
5173
|
const roadmapMd = exists(roadmapPath) ? readFile(roadmapPath) : null;
|
|
5056
5174
|
const roadmap = roadmapStats(roadmapMd, items.length);
|
|
5057
5175
|
const mappedModules = loadMappedModules(dir);
|
|
@@ -5232,6 +5350,15 @@ function renderExplanationHuman(exp) {
|
|
|
5232
5350
|
for (const m of exp.missingKnowledge) lines.push(`- ${m}`);
|
|
5233
5351
|
lines.push("");
|
|
5234
5352
|
}
|
|
5353
|
+
const assessment = assessPhase(exp);
|
|
5354
|
+
lines.push("## Phase");
|
|
5355
|
+
lines.push(`- Phase: ${assessment.phase}`);
|
|
5356
|
+
if (assessment.reasons.length > 0) {
|
|
5357
|
+
lines.push("- Reason:");
|
|
5358
|
+
for (const r of assessment.reasons) lines.push(` - ${r}`);
|
|
5359
|
+
}
|
|
5360
|
+
if (assessment.nextStep) lines.push(`- Next step: ${assessment.nextStep}`);
|
|
5361
|
+
lines.push("");
|
|
5235
5362
|
if (exp.suggestedNextSteps.length > 0) {
|
|
5236
5363
|
lines.push("## Suggested Next Steps");
|
|
5237
5364
|
exp.suggestedNextSteps.forEach((s, i) => lines.push(`${i + 1}. ${s}`));
|
|
@@ -5245,10 +5372,10 @@ function renderExplanationAgent(exp) {
|
|
|
5245
5372
|
}
|
|
5246
5373
|
|
|
5247
5374
|
// src/commands/explain.ts
|
|
5248
|
-
var
|
|
5375
|
+
var ARCH_DIR5 = "knowledge";
|
|
5249
5376
|
var CONFIG_PATH3 = ".kaddo/config.yml";
|
|
5250
5377
|
function readKnowledge(dir) {
|
|
5251
|
-
const knowledgePath = join(dir,
|
|
5378
|
+
const knowledgePath = join(dir, ARCH_DIR5, "knowledge.md");
|
|
5252
5379
|
if (!exists(knowledgePath)) return null;
|
|
5253
5380
|
try {
|
|
5254
5381
|
const raw = readFile(knowledgePath);
|
|
@@ -5259,7 +5386,7 @@ function readKnowledge(dir) {
|
|
|
5259
5386
|
}
|
|
5260
5387
|
}
|
|
5261
5388
|
function readRoadmap(dir) {
|
|
5262
|
-
const roadmapPath = join(dir,
|
|
5389
|
+
const roadmapPath = join(dir, ARCH_DIR5, "roadmap.md");
|
|
5263
5390
|
if (!exists(roadmapPath)) return null;
|
|
5264
5391
|
try {
|
|
5265
5392
|
const raw = readFile(roadmapPath);
|
|
@@ -5408,7 +5535,7 @@ function runExplain(opts) {
|
|
|
5408
5535
|
explainProject(dir, mode);
|
|
5409
5536
|
return;
|
|
5410
5537
|
}
|
|
5411
|
-
const archDir = join(dir,
|
|
5538
|
+
const archDir = join(dir, ARCH_DIR5);
|
|
5412
5539
|
if (!exists(archDir)) {
|
|
5413
5540
|
console.error("No knowledge/ directory found. Run `kaddo init` first.");
|
|
5414
5541
|
process.exit(1);
|
|
@@ -5433,7 +5560,7 @@ function runExplain(opts) {
|
|
|
5433
5560
|
// src/core/context-pack.ts
|
|
5434
5561
|
import matter3 from "gray-matter";
|
|
5435
5562
|
var CONTEXT_PACK_VERSION = "1";
|
|
5436
|
-
var
|
|
5563
|
+
var ARCH_DIR6 = "knowledge";
|
|
5437
5564
|
function readScanJson(dir) {
|
|
5438
5565
|
const scanPath = join(dir, ".kaddo", "scan.json");
|
|
5439
5566
|
if (!exists(scanPath)) return null;
|
|
@@ -5449,7 +5576,7 @@ function firstParagraph(markdown) {
|
|
|
5449
5576
|
return para ?? "";
|
|
5450
5577
|
}
|
|
5451
5578
|
function readMarkdownSummary(dir, file) {
|
|
5452
|
-
const p2 = join(dir,
|
|
5579
|
+
const p2 = join(dir, ARCH_DIR6, file);
|
|
5453
5580
|
if (!exists(p2)) return null;
|
|
5454
5581
|
try {
|
|
5455
5582
|
return firstParagraph(readFile(p2));
|
|
@@ -5527,7 +5654,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
5527
5654
|
missing.push("Scan baseline missing. Run `kaddo scan` for better context.");
|
|
5528
5655
|
}
|
|
5529
5656
|
const detected = scanJson?.detected ?? {};
|
|
5530
|
-
const inventoryAvailable = exists(join(dir,
|
|
5657
|
+
const inventoryAvailable = exists(join(dir, ARCH_DIR6, "inventory.md"));
|
|
5531
5658
|
if (!inventoryAvailable) {
|
|
5532
5659
|
missing.push("No technical inventory found. Run `kaddo scan` to generate it.");
|
|
5533
5660
|
}
|
|
@@ -5539,8 +5666,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
5539
5666
|
if (!roadmapSummary) {
|
|
5540
5667
|
missing.push("No roadmap baseline found.");
|
|
5541
5668
|
}
|
|
5542
|
-
const
|
|
5543
|
-
const allArtifacts = exists(archPath) ? readArtifacts(archPath) : [];
|
|
5669
|
+
const allArtifacts = discoverKnowledge(dir);
|
|
5544
5670
|
const workItems = allArtifacts.filter(
|
|
5545
5671
|
(a) => isDeliveryWorkItem(a) && isActiveState(lifecycleStateOf({ status: a.status, filePath: a.filePath }))
|
|
5546
5672
|
);
|
|
@@ -5548,7 +5674,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
5548
5674
|
missing.push("No work items found.");
|
|
5549
5675
|
}
|
|
5550
5676
|
const state = config.project.state;
|
|
5551
|
-
const roadmapPath = join(dir,
|
|
5677
|
+
const roadmapPath = join(dir, ARCH_DIR6, "delivery", "roadmap.md");
|
|
5552
5678
|
const materialized = allArtifacts.filter(
|
|
5553
5679
|
(a) => a.filePath.replace(/\\/g, "/").includes("/delivery/work-items/") && Boolean(a.type)
|
|
5554
5680
|
).length;
|
|
@@ -5562,6 +5688,28 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
5562
5688
|
}
|
|
5563
5689
|
const mappedModules = loadMappedModules(dir);
|
|
5564
5690
|
const layers = knowledgeLayers(dir);
|
|
5691
|
+
const allWorkItems = allArtifacts.filter((a) => a.isWorkItem);
|
|
5692
|
+
const wiWithOwnership = allWorkItems.filter((a) => a.codeGlobs.length > 0).length;
|
|
5693
|
+
const phase = assessPhase({
|
|
5694
|
+
layers,
|
|
5695
|
+
roadmap,
|
|
5696
|
+
workItems: {
|
|
5697
|
+
total: allWorkItems.length,
|
|
5698
|
+
byState: lifecycleCounts(
|
|
5699
|
+
allWorkItems.map((a) => lifecycleStateOf({ status: a.status, filePath: a.filePath }))
|
|
5700
|
+
),
|
|
5701
|
+
items: allWorkItems.map((a) => ({
|
|
5702
|
+
id: a.id || a.title,
|
|
5703
|
+
title: a.title,
|
|
5704
|
+
lifecycle: lifecycleStateOf({ status: a.status, filePath: a.filePath })
|
|
5705
|
+
}))
|
|
5706
|
+
},
|
|
5707
|
+
ownership: {
|
|
5708
|
+
workItemsTotal: allWorkItems.length,
|
|
5709
|
+
workItemsWithOwnership: wiWithOwnership,
|
|
5710
|
+
workItemsMissingOwnership: allWorkItems.length - wiWithOwnership
|
|
5711
|
+
}
|
|
5712
|
+
});
|
|
5565
5713
|
return {
|
|
5566
5714
|
version: CONTEXT_PACK_VERSION,
|
|
5567
5715
|
generatedAt: now.toISOString(),
|
|
@@ -5590,6 +5738,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
5590
5738
|
},
|
|
5591
5739
|
layers,
|
|
5592
5740
|
roadmap,
|
|
5741
|
+
phase,
|
|
5593
5742
|
deliveryMix,
|
|
5594
5743
|
mappedModules,
|
|
5595
5744
|
missing,
|
|
@@ -5627,6 +5776,21 @@ function renderContextPack(pack) {
|
|
|
5627
5776
|
`- Structure: ${project.structure}`
|
|
5628
5777
|
].join("\n") + "\n"
|
|
5629
5778
|
);
|
|
5779
|
+
parts.push("## Current Phase\n");
|
|
5780
|
+
parts.push(`Phase: ${pack.phase.phase}
|
|
5781
|
+
`);
|
|
5782
|
+
if (pack.phase.reasons.length > 0) {
|
|
5783
|
+
parts.push("Reason:\n");
|
|
5784
|
+
parts.push(pack.phase.reasons.map((r) => `- ${r}`).join("\n") + "\n");
|
|
5785
|
+
}
|
|
5786
|
+
if (pack.phase.recommendedAgents.length > 0) {
|
|
5787
|
+
parts.push(`Recommended next: ${pack.phase.recommendedAgents.join(", ")}
|
|
5788
|
+
`);
|
|
5789
|
+
}
|
|
5790
|
+
if (pack.phase.nextStep) {
|
|
5791
|
+
parts.push(`Next step: ${pack.phase.nextStep}
|
|
5792
|
+
`);
|
|
5793
|
+
}
|
|
5630
5794
|
parts.push("## Knowledge Layers\n");
|
|
5631
5795
|
parts.push(
|
|
5632
5796
|
"Project knowledge is organized in four layers: **Business \u2192 Product \u2192 Tech \u2192 Delivery**.\n"
|
|
@@ -5942,19 +6106,12 @@ import { parse as parseYaml9 } from "yaml";
|
|
|
5942
6106
|
function slugify2(s) {
|
|
5943
6107
|
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
5944
6108
|
}
|
|
5945
|
-
function isWorkItem(a) {
|
|
5946
|
-
return a.filePath.replace(/\\/g, "/").includes("/delivery/work-items/") && Boolean(a.type);
|
|
5947
|
-
}
|
|
5948
6109
|
function toActive(a) {
|
|
5949
6110
|
const id = a.id || a.title || "WI";
|
|
5950
6111
|
return { id, title: a.title || id, type: a.type, slug: slugify2(a.title || id) };
|
|
5951
6112
|
}
|
|
5952
6113
|
function activeWorkItems(dir) {
|
|
5953
|
-
|
|
5954
|
-
if (!exists(archDir)) return [];
|
|
5955
|
-
return readArtifacts(archDir).filter(
|
|
5956
|
-
(a) => isWorkItem(a) && lifecycleStateOf({ status: a.status, filePath: a.filePath }) === "in-progress"
|
|
5957
|
-
).map(toActive);
|
|
6114
|
+
return discoverWorkItems(dir).filter((a) => a.lifecycle === "in-progress").map(toActive);
|
|
5958
6115
|
}
|
|
5959
6116
|
function branchPrefix(type) {
|
|
5960
6117
|
switch (type) {
|
|
@@ -6033,64 +6190,19 @@ function runUnderstand() {
|
|
|
6033
6190
|
);
|
|
6034
6191
|
}
|
|
6035
6192
|
console.log(renderUnderstandTerminal(plan));
|
|
6036
|
-
const
|
|
6037
|
-
const
|
|
6038
|
-
const groupAgents = (AGENT_GROUPS[group] ?? []).map((a) => a.replace(/\.md$/, ""));
|
|
6039
|
-
const NEXT_STEPS = {
|
|
6040
|
-
business: ["Use business-agent \u2192 refine knowledge/business/business.md"],
|
|
6041
|
-
product: [
|
|
6042
|
-
"Use bootstrap-agent \u2192 refine knowledge/product/product.md",
|
|
6043
|
-
"Use capability-agent \u2192 knowledge/product/capabilities.md"
|
|
6044
|
-
],
|
|
6045
|
-
tech: [
|
|
6046
|
-
"Use architecture-agent \u2192 knowledge/tech/current-state.md (reality)",
|
|
6047
|
-
"Use codebase-agent \u2192 knowledge/tech/codebase.md (intent)",
|
|
6048
|
-
"Record decisions as ADRs in knowledge/tech/decisions/"
|
|
6049
|
-
],
|
|
6050
|
-
delivery: [
|
|
6051
|
-
"Use roadmap-agent \u2192 knowledge/delivery/roadmap.md",
|
|
6052
|
-
"Run `kaddo create --from roadmap` (candidates \u2192 Work Items)",
|
|
6053
|
-
"Use work-item-agent \u2192 refine into knowledge/delivery/work-items/",
|
|
6054
|
-
"Use implementation-agent \u2192 implement, then `kaddo scan`, `kaddo owners suggest`, `kaddo guard`"
|
|
6055
|
-
],
|
|
6056
|
-
utilities: ["Use legacy-agent to surface risks and unknowns"]
|
|
6057
|
-
};
|
|
6058
|
-
if (roadmapHasUnmaterializedCandidates(dir)) {
|
|
6059
|
-
const { candidates, materialized, remaining } = pack.roadmap;
|
|
6060
|
-
console.log("");
|
|
6061
|
-
console.log(
|
|
6062
|
-
`The roadmap has ${remaining} unmaterialized Work Item candidate(s) (${candidates} candidate(s), ${materialized} materialized).`
|
|
6063
|
-
);
|
|
6064
|
-
console.log(" \u2192 Run `kaddo create --from roadmap`, or use the work-item-agent to");
|
|
6065
|
-
console.log(" materialize them into knowledge/delivery/work-items/.");
|
|
6066
|
-
}
|
|
6193
|
+
const exp = buildProjectExplanation(dir);
|
|
6194
|
+
const assessment = assessPhase(exp);
|
|
6067
6195
|
console.log("");
|
|
6068
|
-
console.log(`Current phase: ${phase}`);
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
let n = 3;
|
|
6073
|
-
for (const step of NEXT_STEPS[group] ?? []) console.log(` ${n++}. ${step}`);
|
|
6074
|
-
if (groupAgents.length > 0) {
|
|
6075
|
-
console.log(`Agents for this phase: ${groupAgents.join(", ")}`);
|
|
6196
|
+
console.log(`Current phase: ${assessment.phase}`);
|
|
6197
|
+
if (assessment.reasons.length > 0) {
|
|
6198
|
+
console.log("Reason:");
|
|
6199
|
+
for (const r of assessment.reasons) console.log(` - ${r}`);
|
|
6076
6200
|
}
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
console.log(
|
|
6082
|
-
console.log(` Draft: ${bs.draft} Ready: ${bs.ready} In Progress: ${bs["in-progress"]} Blocked: ${bs.blocked}`);
|
|
6083
|
-
const firstReady = exp.workItems.items.find((i) => i.lifecycle === "ready");
|
|
6084
|
-
const firstInProgress = exp.workItems.items.find((i) => i.lifecycle === "in-progress");
|
|
6085
|
-
if (firstInProgress) {
|
|
6086
|
-
console.log(` \u2192 Continue ${firstInProgress.id} \u2014 ${firstInProgress.title} (in progress).`);
|
|
6087
|
-
} else if (firstReady) {
|
|
6088
|
-
console.log(` \u2192 Recommended next step: start ${firstReady.id} \u2014 ${firstReady.title}.`);
|
|
6089
|
-
} else if (bs.draft > 0) {
|
|
6090
|
-
console.log(" \u2192 Refine a draft Work Item to `ready` (scope + acceptance defined).");
|
|
6091
|
-
} else if (bs.blocked > 0) {
|
|
6092
|
-
console.log(" \u2192 All active work is blocked. Resolve the blockers to move forward.");
|
|
6093
|
-
}
|
|
6201
|
+
if (assessment.recommendedAgents.length > 0) {
|
|
6202
|
+
console.log(`Recommended: ${assessment.recommendedAgents.join(", ")}`);
|
|
6203
|
+
}
|
|
6204
|
+
if (assessment.nextStep) {
|
|
6205
|
+
console.log(`Next step: ${assessment.nextStep}`);
|
|
6094
6206
|
}
|
|
6095
6207
|
const active = activeWorkItems(dir);
|
|
6096
6208
|
if (active.length > 0) {
|
|
@@ -6257,9 +6369,9 @@ function classify(declaredType, declaredLevel, touchedFiles) {
|
|
|
6257
6369
|
}
|
|
6258
6370
|
|
|
6259
6371
|
// src/commands/classify.ts
|
|
6260
|
-
var
|
|
6372
|
+
var ARCH_DIR7 = "knowledge";
|
|
6261
6373
|
function findActiveWorkItem(dir) {
|
|
6262
|
-
const archDir = join(dir,
|
|
6374
|
+
const archDir = join(dir, ARCH_DIR7);
|
|
6263
6375
|
if (!exists(archDir)) return null;
|
|
6264
6376
|
const artifacts = readArtifacts(archDir);
|
|
6265
6377
|
return artifacts.filter((a) => a.status === "in-progress" && a.type !== "current-state" && a.type !== "roadmap").sort((a, b) => {
|
|
@@ -6325,7 +6437,7 @@ async function runClassify(opts = {}) {
|
|
|
6325
6437
|
|
|
6326
6438
|
// src/commands/status.ts
|
|
6327
6439
|
import { parse as parseYaml10 } from "yaml";
|
|
6328
|
-
var
|
|
6440
|
+
var ARCH_DIR8 = "knowledge";
|
|
6329
6441
|
var CONFIG_PATH4 = ".kaddo/config.yml";
|
|
6330
6442
|
function loadConfig3(dir) {
|
|
6331
6443
|
const p2 = join(dir, CONFIG_PATH4);
|
|
@@ -6338,12 +6450,12 @@ function loadConfig3(dir) {
|
|
|
6338
6450
|
}
|
|
6339
6451
|
function runStatus() {
|
|
6340
6452
|
const dir = cwd();
|
|
6341
|
-
if (!exists(join(dir,
|
|
6453
|
+
if (!exists(join(dir, ARCH_DIR8))) {
|
|
6342
6454
|
console.error("No knowledge/ directory found. Run `kaddo init` first.");
|
|
6343
6455
|
process.exit(1);
|
|
6344
6456
|
}
|
|
6345
6457
|
const config = loadConfig3(dir);
|
|
6346
|
-
const artifacts = readArtifacts(join(dir,
|
|
6458
|
+
const artifacts = readArtifacts(join(dir, ARCH_DIR8));
|
|
6347
6459
|
const ignores = loadIgnores(dir);
|
|
6348
6460
|
const workItems = artifacts.filter(
|
|
6349
6461
|
(a) => a.type !== "current-state" && a.type !== "roadmap"
|
|
@@ -6389,7 +6501,7 @@ function runStatus() {
|
|
|
6389
6501
|
|
|
6390
6502
|
// src/commands/learn.ts
|
|
6391
6503
|
import matter4 from "gray-matter";
|
|
6392
|
-
var
|
|
6504
|
+
var ARCH_DIR9 = "knowledge";
|
|
6393
6505
|
var WORK_ITEMS_DIR2 = "knowledge/delivery/work-items";
|
|
6394
6506
|
function findWorkItemFile(dir, id) {
|
|
6395
6507
|
const wiDir = join(dir, WORK_ITEMS_DIR2);
|
|
@@ -6430,12 +6542,12 @@ ${learning.trim()}
|
|
|
6430
6542
|
}
|
|
6431
6543
|
async function runLearn(artifactId) {
|
|
6432
6544
|
const dir = cwd();
|
|
6433
|
-
if (!exists(join(dir,
|
|
6545
|
+
if (!exists(join(dir, ARCH_DIR9))) {
|
|
6434
6546
|
console.error("No knowledge/ directory found. Run `kaddo init` first.");
|
|
6435
6547
|
process.exit(1);
|
|
6436
6548
|
}
|
|
6437
6549
|
intro2("kaddo learn");
|
|
6438
|
-
const artifacts = readArtifacts(join(dir,
|
|
6550
|
+
const artifacts = readArtifacts(join(dir, ARCH_DIR9));
|
|
6439
6551
|
const inProgress = artifacts.filter(
|
|
6440
6552
|
(a) => a.status === "in-progress" && a.type !== "current-state" && a.type !== "roadmap"
|
|
6441
6553
|
);
|
|
@@ -6478,7 +6590,7 @@ async function runLearn(artifactId) {
|
|
|
6478
6590
|
}
|
|
6479
6591
|
|
|
6480
6592
|
// src/commands/history.ts
|
|
6481
|
-
var
|
|
6593
|
+
var ARCH_DIR10 = "knowledge";
|
|
6482
6594
|
function formatRow(a) {
|
|
6483
6595
|
const id = (a.id || "").padEnd(8);
|
|
6484
6596
|
const type = a.type.padEnd(18);
|
|
@@ -6489,11 +6601,11 @@ function formatRow(a) {
|
|
|
6489
6601
|
}
|
|
6490
6602
|
function runHistory(opts = {}) {
|
|
6491
6603
|
const dir = cwd();
|
|
6492
|
-
if (!exists(join(dir,
|
|
6604
|
+
if (!exists(join(dir, ARCH_DIR10))) {
|
|
6493
6605
|
console.error("No knowledge/ directory found. Run `kaddo init` first.");
|
|
6494
6606
|
process.exit(1);
|
|
6495
6607
|
}
|
|
6496
|
-
let artifacts = readArtifacts(join(dir,
|
|
6608
|
+
let artifacts = readArtifacts(join(dir, ARCH_DIR10)).filter(
|
|
6497
6609
|
(a) => a.type !== "current-state" && a.type !== "roadmap"
|
|
6498
6610
|
);
|
|
6499
6611
|
if (opts.domain) {
|
|
@@ -6654,42 +6766,23 @@ function runAdd(moduleName, opts = {}, dir = cwd()) {
|
|
|
6654
6766
|
|
|
6655
6767
|
// src/core/ownership-suggest.ts
|
|
6656
6768
|
import matter5 from "gray-matter";
|
|
6657
|
-
var WORK_ITEMS_DIR3 = "knowledge/delivery/work-items";
|
|
6658
6769
|
var SCAN_PATH = ".kaddo/scan.json";
|
|
6659
6770
|
function toStringArray3(value) {
|
|
6660
6771
|
return Array.isArray(value) ? value.map((v) => String(v)).filter(Boolean) : [];
|
|
6661
6772
|
}
|
|
6662
|
-
function parseOwnershipArtifact(filePath, relPath, raw) {
|
|
6663
|
-
try {
|
|
6664
|
-
const { data } = matter5(raw);
|
|
6665
|
-
const codeGlobs = toStringArray3(data.code);
|
|
6666
|
-
return {
|
|
6667
|
-
filePath,
|
|
6668
|
-
relPath: relPath.split("\\").join("/"),
|
|
6669
|
-
id: String(data.id ?? ""),
|
|
6670
|
-
title: String(data.title ?? ""),
|
|
6671
|
-
type: String(data.type ?? ""),
|
|
6672
|
-
domains: toStringArray3(data.domains),
|
|
6673
|
-
capabilities: toStringArray3(data.capabilities),
|
|
6674
|
-
codeGlobs,
|
|
6675
|
-
source: data.source ? String(data.source) : void 0,
|
|
6676
|
-
hasOwnership: codeGlobs.length > 0
|
|
6677
|
-
};
|
|
6678
|
-
} catch {
|
|
6679
|
-
return null;
|
|
6680
|
-
}
|
|
6681
|
-
}
|
|
6682
6773
|
function findWorkItemArtifacts(dir) {
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6774
|
+
return discoverWorkItems(dir).map((a) => ({
|
|
6775
|
+
filePath: a.filePath,
|
|
6776
|
+
relPath: a.relPath,
|
|
6777
|
+
id: a.id,
|
|
6778
|
+
title: a.title,
|
|
6779
|
+
type: a.type,
|
|
6780
|
+
domains: a.domains,
|
|
6781
|
+
capabilities: a.capabilities,
|
|
6782
|
+
codeGlobs: a.codeGlobs,
|
|
6783
|
+
source: a.source || void 0,
|
|
6784
|
+
hasOwnership: a.codeGlobs.length > 0
|
|
6785
|
+
}));
|
|
6693
6786
|
}
|
|
6694
6787
|
function artifactsMissingOwnership(artifacts) {
|
|
6695
6788
|
return artifacts.filter((a) => !a.hasOwnership);
|
|
@@ -6833,16 +6926,24 @@ async function runOwnersSuggest() {
|
|
|
6833
6926
|
intro2("kaddo owners suggest");
|
|
6834
6927
|
const artifacts = findWorkItemArtifacts(dir);
|
|
6835
6928
|
if (artifacts.length === 0) {
|
|
6836
|
-
log2.warn(
|
|
6929
|
+
log2.warn(
|
|
6930
|
+
"No Work Items found under knowledge/delivery/work-items/ (searched recursively, including draft/ ready/ in-progress/ \u2026)."
|
|
6931
|
+
);
|
|
6932
|
+
log2.info("Create one with `kaddo create` or `kaddo create --from roadmap`.");
|
|
6837
6933
|
outro2("Nothing to do.");
|
|
6838
6934
|
return;
|
|
6839
6935
|
}
|
|
6840
6936
|
const missing = artifactsMissingOwnership(artifacts);
|
|
6841
6937
|
if (missing.length === 0) {
|
|
6842
|
-
log2.
|
|
6938
|
+
log2.success(
|
|
6939
|
+
`Found ${artifacts.length} Work Item${artifacts.length === 1 ? "" : "s"}; all already declare code ownership.`
|
|
6940
|
+
);
|
|
6843
6941
|
outro2("Nothing to do.");
|
|
6844
6942
|
return;
|
|
6845
6943
|
}
|
|
6944
|
+
log2.info(
|
|
6945
|
+
`Found ${artifacts.length} Work Item${artifacts.length === 1 ? "" : "s"}; ${missing.length} missing code ownership.`
|
|
6946
|
+
);
|
|
6846
6947
|
const relPath = missing.length === 1 ? missing[0].relPath : await select2({
|
|
6847
6948
|
message: "Which artifact needs ownership?",
|
|
6848
6949
|
options: missing.map((a) => ({
|
|
@@ -8769,7 +8870,7 @@ async function runBootstrap(dir = cwd()) {
|
|
|
8769
8870
|
const result = bootstrap(dir);
|
|
8770
8871
|
console.log("");
|
|
8771
8872
|
console.log("Bootstrap layers:");
|
|
8772
|
-
for (const
|
|
8873
|
+
for (const layer2 of result.layers) console.log(` \u2713 ${layer2}`);
|
|
8773
8874
|
console.log("");
|
|
8774
8875
|
if (result.written.length > 0) {
|
|
8775
8876
|
console.log("Created:");
|