@kaddo/cli 3.8.0 → 3.9.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 +80 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -725,6 +725,17 @@ async function runScan() {
|
|
|
725
725
|
}
|
|
726
726
|
|
|
727
727
|
// src/core/knowledge-levels.ts
|
|
728
|
+
var WORK_ITEM_TYPES = ["feature", "bugfix", "hotfix", "spike", "chore"];
|
|
729
|
+
var TYPE_ALIASES = {
|
|
730
|
+
setup: "chore",
|
|
731
|
+
maintenance: "chore",
|
|
732
|
+
tooling: "chore",
|
|
733
|
+
infrastructure: "chore",
|
|
734
|
+
infra: "chore",
|
|
735
|
+
config: "chore",
|
|
736
|
+
configuration: "chore",
|
|
737
|
+
refactor: "chore"
|
|
738
|
+
};
|
|
728
739
|
var LEVELS = {
|
|
729
740
|
K0: {
|
|
730
741
|
level: "K0",
|
|
@@ -879,7 +890,9 @@ var TYPE_TO_LEVEL = {
|
|
|
879
890
|
hotfix: "K1",
|
|
880
891
|
bugfix: "K2",
|
|
881
892
|
feature: "K2",
|
|
882
|
-
spike: "K3"
|
|
893
|
+
spike: "K3",
|
|
894
|
+
// Chores are maintenance/config/tooling work — low ceremony (problem + expected result).
|
|
895
|
+
chore: "K1"
|
|
883
896
|
};
|
|
884
897
|
function getLevel(level) {
|
|
885
898
|
return LEVELS[level];
|
|
@@ -888,7 +901,13 @@ function getLevelForType(type) {
|
|
|
888
901
|
return TYPE_TO_LEVEL[type];
|
|
889
902
|
}
|
|
890
903
|
function isValidType(type) {
|
|
891
|
-
return
|
|
904
|
+
return WORK_ITEM_TYPES.includes(type);
|
|
905
|
+
}
|
|
906
|
+
function normalizeType(raw) {
|
|
907
|
+
if (!raw) return null;
|
|
908
|
+
const t = raw.trim().toLowerCase();
|
|
909
|
+
if (isValidType(t)) return t;
|
|
910
|
+
return TYPE_ALIASES[t] ?? null;
|
|
892
911
|
}
|
|
893
912
|
|
|
894
913
|
// src/modules/adr.ts
|
|
@@ -1832,6 +1851,9 @@ Produce a roadmap where each initiative includes:
|
|
|
1832
1851
|
7. Dependencies.
|
|
1833
1852
|
8. Why this comes now.
|
|
1834
1853
|
9. Candidate work items (each with type, suggested knowledge level, expected value, notes).
|
|
1854
|
+
Use only the official Work Item types: \`feature\`, \`bugfix\`, \`hotfix\`, \`spike\`, \`chore\`.
|
|
1855
|
+
Use \`chore\` for technical/maintenance/tooling/config/infra work (e.g. "Initialize
|
|
1856
|
+
TypeScript project", "Configure Vitest", "Setup CI") \u2014 do not label such work \`feature\`.
|
|
1835
1857
|
10. Open questions.
|
|
1836
1858
|
|
|
1837
1859
|
Then add a suggested execution order, risks and constraints, a "Not Now" list, and the
|
|
@@ -2133,12 +2155,14 @@ A refined Work Item intended to be saved under the lifecycle workspace:
|
|
|
2133
2155
|
|
|
2134
2156
|
1. Restate the problem in one clear sentence.
|
|
2135
2157
|
2. Split the candidate if it is too large for a single Work Item.
|
|
2136
|
-
3.
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2158
|
+
3. Preserve the candidate's type (\`feature\`, \`bugfix\`, \`hotfix\`, \`spike\`, \`chore\`).
|
|
2159
|
+
Keep \`chore\` for maintenance/tooling/config/infra work \u2014 never upgrade a chore to a feature.
|
|
2160
|
+
4. Validate the Knowledge Level (K0\u2013K4) and propose a different one if needed.
|
|
2161
|
+
5. Propose acceptance criteria.
|
|
2162
|
+
6. Propose Out of scope and Validation sections.
|
|
2163
|
+
7. Propose a Definition of Done.
|
|
2164
|
+
8. Identify open questions and assumptions.
|
|
2165
|
+
9. Suggest ownership candidates (code globs) if evident.
|
|
2142
2166
|
|
|
2143
2167
|
## Constraints
|
|
2144
2168
|
|
|
@@ -3675,20 +3699,21 @@ async function runCreate(type, opts = {}) {
|
|
|
3675
3699
|
}
|
|
3676
3700
|
if (!type) {
|
|
3677
3701
|
console.error("Missing work item type.");
|
|
3678
|
-
console.error("Usage: kaddo create <feature|bugfix|hotfix|spike> [--from roadmap]");
|
|
3702
|
+
console.error("Usage: kaddo create <feature|bugfix|hotfix|spike|chore> [--from roadmap]");
|
|
3679
3703
|
process.exit(1);
|
|
3680
3704
|
}
|
|
3681
3705
|
const modWorkItemType = findWorkItemType(type);
|
|
3682
3706
|
if (modWorkItemType) {
|
|
3683
3707
|
return runCreateModule(dir, modWorkItemType);
|
|
3684
3708
|
}
|
|
3685
|
-
|
|
3709
|
+
const workItemType = normalizeType(type);
|
|
3710
|
+
if (!workItemType) {
|
|
3686
3711
|
console.error(`Unknown work item type: "${type}"`);
|
|
3687
|
-
console.error("Valid types: feature, bugfix, hotfix, spike");
|
|
3712
|
+
console.error("Valid types: feature, bugfix, hotfix, spike, chore");
|
|
3713
|
+
console.error("Aliases: setup, maintenance, tooling, infrastructure, refactor \u2192 chore");
|
|
3688
3714
|
console.error("Module types: adr, incident, rfc, migration, legacy (run `kaddo add <module>` first)");
|
|
3689
3715
|
process.exit(1);
|
|
3690
3716
|
}
|
|
3691
|
-
const workItemType = type;
|
|
3692
3717
|
const level = getLevelForType(workItemType);
|
|
3693
3718
|
const levelDef = getLevel(level);
|
|
3694
3719
|
intro2(`kaddo create ${workItemType}`);
|
|
@@ -3696,7 +3721,7 @@ async function runCreate(type, opts = {}) {
|
|
|
3696
3721
|
printStateGuidance(dir);
|
|
3697
3722
|
const title = await text2({
|
|
3698
3723
|
message: "Title for this work item",
|
|
3699
|
-
placeholder: `e.g. ${workItemType === "feature" ? "Add email verification to checkout" : workItemType === "hotfix" ? "Fix null pointer in payment handler" : workItemType === "bugfix" ? "Fix broken pagination on orders list" : "Explore caching strategies for API responses"}`,
|
|
3724
|
+
placeholder: `e.g. ${workItemType === "feature" ? "Add email verification to checkout" : workItemType === "hotfix" ? "Fix null pointer in payment handler" : workItemType === "bugfix" ? "Fix broken pagination on orders list" : workItemType === "chore" ? "Configure Vitest and CI pipeline" : "Explore caching strategies for API responses"}`,
|
|
3700
3725
|
validate: (v) => v.trim().length === 0 ? "Title is required." : void 0
|
|
3701
3726
|
});
|
|
3702
3727
|
const answers = {};
|
|
@@ -3765,9 +3790,7 @@ ${body}`;
|
|
|
3765
3790
|
}
|
|
3766
3791
|
var K_LEVEL_RE = /^K[0-4]$/;
|
|
3767
3792
|
function resolveCandidateType(candidate, cliType) {
|
|
3768
|
-
|
|
3769
|
-
if (cliType && isValidType(cliType)) return cliType;
|
|
3770
|
-
return null;
|
|
3793
|
+
return normalizeType(candidate.type) ?? normalizeType(cliType);
|
|
3771
3794
|
}
|
|
3772
3795
|
function resolveCandidateLevel(candidate, type) {
|
|
3773
3796
|
const suggested = candidate.suggestedKnowledgeLevel?.trim().toUpperCase();
|
|
@@ -3929,7 +3952,8 @@ async function runCreateFromRoadmap(dir, cliType) {
|
|
|
3929
3952
|
{ value: "feature", label: "feature" },
|
|
3930
3953
|
{ value: "bugfix", label: "bugfix" },
|
|
3931
3954
|
{ value: "hotfix", label: "hotfix" },
|
|
3932
|
-
{ value: "spike", label: "spike" }
|
|
3955
|
+
{ value: "spike", label: "spike" },
|
|
3956
|
+
{ value: "chore", label: "chore" }
|
|
3933
3957
|
]
|
|
3934
3958
|
});
|
|
3935
3959
|
}
|
|
@@ -4725,7 +4749,7 @@ var STRUCTURED_TYPES = {
|
|
|
4725
4749
|
]),
|
|
4726
4750
|
Delivery: /* @__PURE__ */ new Set([])
|
|
4727
4751
|
};
|
|
4728
|
-
var
|
|
4752
|
+
var WORK_ITEM_TYPES2 = /* @__PURE__ */ new Set([
|
|
4729
4753
|
"work-item",
|
|
4730
4754
|
"feature",
|
|
4731
4755
|
"bugfix",
|
|
@@ -4738,7 +4762,7 @@ function layerForType(type) {
|
|
|
4738
4762
|
if (type === CONSOLIDATED_TYPE.Product || STRUCTURED_TYPES.Product.has(type)) return "Product";
|
|
4739
4763
|
if (type === CONSOLIDATED_TYPE.Tech || STRUCTURED_TYPES.Tech.has(type) || type === "knowledge")
|
|
4740
4764
|
return "Tech";
|
|
4741
|
-
if (type === "roadmap" ||
|
|
4765
|
+
if (type === "roadmap" || WORK_ITEM_TYPES2.has(type)) return "Delivery";
|
|
4742
4766
|
return null;
|
|
4743
4767
|
}
|
|
4744
4768
|
function layerFromPath(filePath) {
|
|
@@ -4769,7 +4793,7 @@ function discoverLayers(dir) {
|
|
|
4769
4793
|
slot.detected.add(basename(a.filePath));
|
|
4770
4794
|
if (layer === "Delivery") {
|
|
4771
4795
|
if (type === "roadmap" || basename(a.filePath) === "roadmap.md") slot.hasRoadmap = true;
|
|
4772
|
-
if (
|
|
4796
|
+
if (WORK_ITEM_TYPES2.has(type) || a.filePath.replace(/\\/g, "/").includes("/delivery/work-items/")) {
|
|
4773
4797
|
if (a.filePath.replace(/\\/g, "/").includes("/delivery/work-items/")) slot.hasWorkItem = true;
|
|
4774
4798
|
}
|
|
4775
4799
|
continue;
|
|
@@ -4989,6 +5013,7 @@ function buildProjectExplanation(dir) {
|
|
|
4989
5013
|
const items = workItemArtifacts.map((a) => ({
|
|
4990
5014
|
id: a.id || a.title,
|
|
4991
5015
|
title: a.title,
|
|
5016
|
+
type: a.type,
|
|
4992
5017
|
status: a.status,
|
|
4993
5018
|
lifecycle: lifecycleStateOf({ status: a.status, filePath: a.filePath }),
|
|
4994
5019
|
initiative: a.initiative,
|
|
@@ -4997,6 +5022,11 @@ function buildProjectExplanation(dir) {
|
|
|
4997
5022
|
domains: a.domains
|
|
4998
5023
|
}));
|
|
4999
5024
|
const byState = lifecycleCounts(items.map((i) => i.lifecycle));
|
|
5025
|
+
const byType = {};
|
|
5026
|
+
for (const i of items) {
|
|
5027
|
+
const t = i.type || "unknown";
|
|
5028
|
+
byType[t] = (byType[t] ?? 0) + 1;
|
|
5029
|
+
}
|
|
5000
5030
|
const initiativeMap = /* @__PURE__ */ new Map();
|
|
5001
5031
|
for (const i of items) {
|
|
5002
5032
|
const name = i.initiative || "Unassigned";
|
|
@@ -5010,6 +5040,7 @@ function buildProjectExplanation(dir) {
|
|
|
5010
5040
|
done: items.filter((i) => i.status === "done").length,
|
|
5011
5041
|
cancelled: items.filter((i) => i.status === "cancelled").length,
|
|
5012
5042
|
byState,
|
|
5043
|
+
byType,
|
|
5013
5044
|
initiatives,
|
|
5014
5045
|
items
|
|
5015
5046
|
};
|
|
@@ -5079,6 +5110,10 @@ function buildProjectExplanation(dir) {
|
|
|
5079
5110
|
function stateLabel(state) {
|
|
5080
5111
|
return state === "pre-ai" ? "pre-ai" : state;
|
|
5081
5112
|
}
|
|
5113
|
+
function typeLabel(type) {
|
|
5114
|
+
const cap = type.charAt(0).toUpperCase() + type.slice(1);
|
|
5115
|
+
return cap.endsWith("s") ? cap : `${cap}s`;
|
|
5116
|
+
}
|
|
5082
5117
|
var LIFECYCLE_LABEL = {
|
|
5083
5118
|
draft: "Draft",
|
|
5084
5119
|
ready: "Ready",
|
|
@@ -5140,6 +5175,12 @@ function renderExplanationHuman(exp) {
|
|
|
5140
5175
|
lines.push(`- ${LIFECYCLE_LABEL[s]}: ${exp.workItems.byState[s]}`);
|
|
5141
5176
|
}
|
|
5142
5177
|
lines.push("");
|
|
5178
|
+
const typeEntries = Object.entries(exp.workItems.byType).sort((a, b) => b[1] - a[1]);
|
|
5179
|
+
if (typeEntries.length > 0) {
|
|
5180
|
+
lines.push("## Work Items by Type");
|
|
5181
|
+
for (const [t, n] of typeEntries) lines.push(`- ${typeLabel(t)}: ${n}`);
|
|
5182
|
+
lines.push("");
|
|
5183
|
+
}
|
|
5143
5184
|
const grouped = exp.workItems.initiatives.filter(
|
|
5144
5185
|
(g) => LIFECYCLE_STATES.some((s) => g.states[s] > 0)
|
|
5145
5186
|
);
|
|
@@ -5512,6 +5553,13 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
5512
5553
|
(a) => a.filePath.replace(/\\/g, "/").includes("/delivery/work-items/") && Boolean(a.type)
|
|
5513
5554
|
).length;
|
|
5514
5555
|
const roadmap = roadmapStats(exists(roadmapPath) ? readFile(roadmapPath) : null, materialized);
|
|
5556
|
+
const deliveryMix = {};
|
|
5557
|
+
for (const a of allArtifacts) {
|
|
5558
|
+
const p2 = a.filePath.replace(/\\/g, "/");
|
|
5559
|
+
if (!a.type || !p2.includes("/delivery/work-items/")) continue;
|
|
5560
|
+
if (!isActiveState(lifecycleStateOf({ status: a.status, filePath: a.filePath }))) continue;
|
|
5561
|
+
deliveryMix[a.type] = (deliveryMix[a.type] ?? 0) + 1;
|
|
5562
|
+
}
|
|
5515
5563
|
const mappedModules = loadMappedModules(dir);
|
|
5516
5564
|
const layers = knowledgeLayers(dir);
|
|
5517
5565
|
return {
|
|
@@ -5542,6 +5590,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
5542
5590
|
},
|
|
5543
5591
|
layers,
|
|
5544
5592
|
roadmap,
|
|
5593
|
+
deliveryMix,
|
|
5545
5594
|
mappedModules,
|
|
5546
5595
|
missing,
|
|
5547
5596
|
handoff: {
|
|
@@ -5643,6 +5692,16 @@ function renderContextPack(pack) {
|
|
|
5643
5692
|
} else {
|
|
5644
5693
|
parts.push("No active work items found.\n");
|
|
5645
5694
|
}
|
|
5695
|
+
const mixEntries = Object.entries(pack.deliveryMix).sort((a, b) => b[1] - a[1]);
|
|
5696
|
+
if (mixEntries.length > 0) {
|
|
5697
|
+
parts.push("## Delivery Mix\n");
|
|
5698
|
+
parts.push("Active Work Items by type:\n");
|
|
5699
|
+
const label = (t) => {
|
|
5700
|
+
const c = t.charAt(0).toUpperCase() + t.slice(1);
|
|
5701
|
+
return c.endsWith("s") ? c : `${c}s`;
|
|
5702
|
+
};
|
|
5703
|
+
parts.push(mixEntries.map(([t, n]) => `- ${label(t)}: ${n}`).join("\n") + "\n");
|
|
5704
|
+
}
|
|
5646
5705
|
parts.push("## Artifacts and Ownership\n");
|
|
5647
5706
|
if (knowledge.artifacts.length > 0) {
|
|
5648
5707
|
const lines = knowledge.artifacts.map(
|
|
@@ -8745,7 +8804,7 @@ program.command("scan").description("Detect project stack and suggest domains").
|
|
|
8745
8804
|
program.command("bootstrap").description("Build the initial knowledge base for a new project (Business \u2192 Product \u2192 Tech \u2192 Delivery)").action(async () => {
|
|
8746
8805
|
await runBootstrap();
|
|
8747
8806
|
});
|
|
8748
|
-
program.command("create [type]").description("Create a work item (feature, bugfix, hotfix, spike). Use --from roadmap to create from a roadmap candidate.").option("--from <source>", "Create from a source artifact (currently: roadmap)").action(async (type, opts) => {
|
|
8807
|
+
program.command("create [type]").description("Create a work item (feature, bugfix, hotfix, spike, chore). Use --from roadmap to create from a roadmap candidate.").option("--from <source>", "Create from a source artifact (currently: roadmap)").action(async (type, opts) => {
|
|
8749
8808
|
await runCreate(type ?? "", opts);
|
|
8750
8809
|
});
|
|
8751
8810
|
program.command("guard").description("Check if modified code has related artifacts that were not updated").option("--staged", "Check only staged files").option("--no-interactive", "Disable interactive ignore prompts").option("--ci", "CI mode: output JSON, no prompts, non-blocking").option("--json", "Output JSON (alias for --ci)").option("--workspace", "Also check local mapped module repos from .kaddo/modules.yml (opt-in)").action(async (opts) => {
|