@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.
Files changed (2) hide show
  1. package/dist/index.js +80 -21
  2. 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 ["feature", "bugfix", "hotfix", "spike"].includes(type);
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. Validate the Knowledge Level (K0\u2013K4) and propose a different one if needed.
2137
- 4. Propose acceptance criteria.
2138
- 5. Propose Out of scope and Validation sections.
2139
- 6. Propose a Definition of Done.
2140
- 7. Identify open questions and assumptions.
2141
- 8. Suggest ownership candidates (code globs) if evident.
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
- if (!isValidType(type)) {
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
- if (candidate.type && isValidType(candidate.type)) return candidate.type;
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 WORK_ITEM_TYPES = /* @__PURE__ */ new Set([
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" || WORK_ITEM_TYPES.has(type)) return "Delivery";
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 (WORK_ITEM_TYPES.has(type) || a.filePath.replace(/\\/g, "/").includes("/delivery/work-items/")) {
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) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/cli",
3
- "version": "3.8.0",
3
+ "version": "3.9.0",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {