@kaddo/cli 3.73.0 → 3.74.1

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/core.js CHANGED
@@ -3686,6 +3686,9 @@ var RECOMMENDED_SKILLS = [...SKILL_GROUPS.delivery, ...SKILL_GROUPS.tech];
3686
3686
  function skillInstallPath(id) {
3687
3687
  return `knowledge/skills/${id}/skill.md`;
3688
3688
  }
3689
+ function skillById(id) {
3690
+ return SKILLS.find((s) => s.id === id);
3691
+ }
3689
3692
 
3690
3693
  // src/agents/responsibility.ts
3691
3694
  var RESPONSIBILITY_MATRIX = {
@@ -7721,6 +7724,194 @@ import fs2 from "fs";
7721
7724
  import path2 from "path";
7722
7725
  import crypto from "crypto";
7723
7726
  import matter7 from "gray-matter";
7727
+
7728
+ // src/core/knowledge-levels.ts
7729
+ var WORK_ITEM_TYPES2 = ["feature", "bugfix", "hotfix", "spike", "chore"];
7730
+ var TYPE_ALIASES = {
7731
+ setup: "chore",
7732
+ maintenance: "chore",
7733
+ tooling: "chore",
7734
+ infrastructure: "chore",
7735
+ infra: "chore",
7736
+ config: "chore",
7737
+ configuration: "chore",
7738
+ refactor: "chore"
7739
+ };
7740
+ var LEVELS = {
7741
+ K0: {
7742
+ level: "K0",
7743
+ description: "Trivial change. No formal knowledge required.",
7744
+ questions: [],
7745
+ qualityGate: []
7746
+ },
7747
+ K1: {
7748
+ level: "K1",
7749
+ description: "Simple fix or hotfix.",
7750
+ questions: [
7751
+ {
7752
+ id: "problem",
7753
+ prompt: "What problem does this fix?",
7754
+ placeholder: "e.g. Button is not clickable on mobile",
7755
+ frontMatterField: "problem",
7756
+ required: true
7757
+ },
7758
+ {
7759
+ id: "expected_result",
7760
+ prompt: "What is the expected result after the fix?",
7761
+ placeholder: "e.g. Button works correctly on all screen sizes",
7762
+ frontMatterField: "expected_result",
7763
+ required: true
7764
+ }
7765
+ ],
7766
+ qualityGate: ["Problem is clear.", "Expected result is defined."]
7767
+ },
7768
+ K2: {
7769
+ level: "K2",
7770
+ description: "Feature or bugfix with functional impact.",
7771
+ questions: [
7772
+ {
7773
+ id: "problem",
7774
+ prompt: "What problem does this solve?",
7775
+ placeholder: "e.g. Users cannot complete checkout without email verification",
7776
+ frontMatterField: "problem",
7777
+ required: true
7778
+ },
7779
+ {
7780
+ id: "expected_result",
7781
+ prompt: "What is the expected result?",
7782
+ placeholder: "e.g. Users can verify email inline during checkout",
7783
+ frontMatterField: "expected_result",
7784
+ required: true
7785
+ },
7786
+ {
7787
+ id: "impact",
7788
+ prompt: "What is the impact if this is not done?",
7789
+ placeholder: "e.g. ~30% of users drop off at checkout",
7790
+ frontMatterField: "impact",
7791
+ required: true
7792
+ },
7793
+ {
7794
+ id: "acceptance_criteria",
7795
+ prompt: "What are the acceptance criteria? (one per line, press Enter twice to finish)",
7796
+ placeholder: "e.g. Email is verified before payment step",
7797
+ frontMatterField: "acceptance_criteria",
7798
+ required: true
7799
+ }
7800
+ ],
7801
+ qualityGate: [
7802
+ "Problem is clear.",
7803
+ "Expected result is defined.",
7804
+ "Impact of not doing it is stated.",
7805
+ "Acceptance criteria are verifiable."
7806
+ ]
7807
+ },
7808
+ K3: {
7809
+ level: "K3",
7810
+ description: "Capability, integration, or significant functional change.",
7811
+ questions: [
7812
+ {
7813
+ id: "problem",
7814
+ prompt: "What problem does this solve?",
7815
+ placeholder: "e.g. Payments need to support multiple providers",
7816
+ frontMatterField: "problem",
7817
+ required: true
7818
+ },
7819
+ {
7820
+ id: "impact",
7821
+ prompt: "What is the impact if this is not done?",
7822
+ placeholder: "e.g. We are locked into a single provider with no fallback",
7823
+ frontMatterField: "impact",
7824
+ required: true
7825
+ },
7826
+ {
7827
+ id: "acceptance_criteria",
7828
+ prompt: "What are the acceptance criteria?",
7829
+ placeholder: "e.g. System routes to provider B when provider A fails",
7830
+ frontMatterField: "acceptance_criteria",
7831
+ required: true
7832
+ },
7833
+ {
7834
+ id: "design",
7835
+ prompt: "What is the proposed design or approach?",
7836
+ placeholder: "e.g. Abstract provider interface + strategy pattern",
7837
+ frontMatterField: "design",
7838
+ required: true
7839
+ }
7840
+ ],
7841
+ qualityGate: [
7842
+ "Problem is clear.",
7843
+ "Impact is stated.",
7844
+ "Acceptance criteria are verifiable.",
7845
+ "Design is sufficient to start."
7846
+ ]
7847
+ },
7848
+ K4: {
7849
+ level: "K4",
7850
+ description: "Architecture change, migration, or high-risk decision.",
7851
+ questions: [
7852
+ {
7853
+ id: "problem",
7854
+ prompt: "What problem does this solve?",
7855
+ placeholder: "e.g. Current auth system does not support SSO",
7856
+ frontMatterField: "problem",
7857
+ required: true
7858
+ },
7859
+ {
7860
+ id: "impact",
7861
+ prompt: "What is the impact if this is not done?",
7862
+ placeholder: "e.g. Enterprise clients cannot onboard",
7863
+ frontMatterField: "impact",
7864
+ required: true
7865
+ },
7866
+ {
7867
+ id: "design",
7868
+ prompt: "What is the proposed design or approach?",
7869
+ placeholder: "e.g. Replace JWT-only auth with OIDC + JWT hybrid",
7870
+ frontMatterField: "design",
7871
+ required: true
7872
+ },
7873
+ {
7874
+ id: "risks",
7875
+ prompt: "What are the main risks?",
7876
+ placeholder: "e.g. Existing sessions may be invalidated during migration",
7877
+ frontMatterField: "risks",
7878
+ required: true
7879
+ }
7880
+ ],
7881
+ qualityGate: [
7882
+ "Problem is clear.",
7883
+ "Impact is stated.",
7884
+ "Design is documented.",
7885
+ "Risks are identified.",
7886
+ "ADR is created or linked if applicable."
7887
+ ]
7888
+ }
7889
+ };
7890
+ var TYPE_TO_LEVEL = {
7891
+ hotfix: "K1",
7892
+ bugfix: "K2",
7893
+ feature: "K2",
7894
+ spike: "K3",
7895
+ // Chores are maintenance/config/tooling work — low ceremony (problem + expected result).
7896
+ chore: "K1"
7897
+ };
7898
+ function getLevel(level) {
7899
+ return LEVELS[level];
7900
+ }
7901
+ function getLevelForType(type) {
7902
+ return TYPE_TO_LEVEL[type];
7903
+ }
7904
+ function isValidType(type) {
7905
+ return WORK_ITEM_TYPES2.includes(type);
7906
+ }
7907
+ function normalizeType(raw) {
7908
+ if (!raw) return null;
7909
+ const t = raw.trim().toLowerCase();
7910
+ if (isValidType(t)) return t;
7911
+ return TYPE_ALIASES[t] ?? null;
7912
+ }
7913
+
7914
+ // src/core/work-item-write.ts
7724
7915
  var WORK_ITEMS_DIR = "knowledge/delivery/work-items";
7725
7916
  var WorkItemWriteError = class extends Error {
7726
7917
  constructor(code, message) {
@@ -8004,11 +8195,33 @@ function getWorkItemForEdit(dir, id) {
8004
8195
  ...editable ? {} : { editableReason: status === "ready" ? "This Work Item is Ready. Reopen it as Draft to edit its scope." : `This Work Item is ${status} and cannot be edited from the structured editor.` }
8005
8196
  };
8006
8197
  }
8198
+ var CAPTURE_SECTIONS = [
8199
+ { field: "problem", heading: "Problem" },
8200
+ { field: "expected_result", heading: "Expected result" },
8201
+ { field: "impact", heading: "Impact" },
8202
+ { field: "acceptance_criteria", heading: "Acceptance criteria", list: true },
8203
+ { field: "design", heading: "Design" },
8204
+ { field: "risks", heading: "Risks" }
8205
+ ];
8206
+ function captureBody(title, type, answers) {
8207
+ const out = [`# ${title}`, "", `> Type: ${type}`];
8208
+ for (const { field, heading, list: list2 } of CAPTURE_SECTIONS) {
8209
+ const value = answers[field]?.trim();
8210
+ if (!value) continue;
8211
+ if (list2) {
8212
+ const items = value.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).map((l) => /^[-*+]\s/.test(l) ? l : `- ${l}`);
8213
+ out.push("", `## ${heading}`, "", items.join("\n"));
8214
+ } else {
8215
+ out.push("", `## ${heading}`, "", value);
8216
+ }
8217
+ }
8218
+ return out.join("\n") + "\n";
8219
+ }
8007
8220
  function createWorkItem(dir, opts) {
8008
8221
  const intent = opts.intent.trim();
8009
8222
  if (!intent) throw new WorkItemWriteError("INVALID_INPUT", "An intent or summary is required.");
8010
- const type = opts.type.trim() || "feature";
8011
- if (!WORK_ITEM_TYPES.has(type)) throw new WorkItemWriteError("INVALID_INPUT", `Unknown Work Item type "${type}".`);
8223
+ const type = normalizeType(opts.type.trim()) ?? "";
8224
+ if (!type) throw new WorkItemWriteError("INVALID_INPUT", `Unknown Work Item type "${opts.type}".`);
8012
8225
  const id = nextWorkItemId(dir);
8013
8226
  const title = intent.split(/\r?\n/)[0].trim().slice(0, 120);
8014
8227
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -8024,7 +8237,9 @@ function createWorkItem(dir, opts) {
8024
8237
  affected_modules: [],
8025
8238
  summary: intent
8026
8239
  };
8027
- const input = {
8240
+ const answers = opts.answers ?? {};
8241
+ const hasAnswers = Object.values(answers).some((v) => v?.trim());
8242
+ const body = hasAnswers ? captureBody(title, type, answers) : freshBody({
8028
8243
  title,
8029
8244
  type,
8030
8245
  summary: intent,
@@ -8036,8 +8251,7 @@ function createWorkItem(dir, opts) {
8036
8251
  decisions: [],
8037
8252
  relatedKnowledge: [],
8038
8253
  scopeConfidence: null
8039
- };
8040
- const body = freshBody(input);
8254
+ });
8041
8255
  const relPath = `${WORK_ITEMS_DIR}/draft/${id}-${slugify2(title)}.md`;
8042
8256
  const filePath = join(dir, relPath);
8043
8257
  if (exists(filePath)) throw new WorkItemWriteError("INVALID_INPUT", `Work Item file already exists: ${relPath}`);
@@ -8047,7 +8261,7 @@ function createWorkItem(dir, opts) {
8047
8261
  }
8048
8262
  function validateInput(input) {
8049
8263
  if (!input.title.trim()) throw new WorkItemWriteError("INVALID_INPUT", "Title is required.");
8050
- if (!WORK_ITEM_TYPES.has(input.type)) throw new WorkItemWriteError("INVALID_INPUT", `Unknown Work Item type "${input.type}".`);
8264
+ if (!isValidType(input.type)) throw new WorkItemWriteError("INVALID_INPUT", `Unknown Work Item type "${input.type}".`);
8051
8265
  }
8052
8266
  function updateWorkItem(dir, id, input, expectedRevision) {
8053
8267
  validateInput(input);
@@ -8125,11 +8339,145 @@ function transitionWorkItem(dir, id, to, expectedRevision) {
8125
8339
  }
8126
8340
  return { revision: revisionOf(nextRaw), status: to, path: targetRel };
8127
8341
  }
8342
+
8343
+ // src/core/work-item-refinement.ts
8344
+ function toCapture(q) {
8345
+ return { id: q.id, prompt: q.prompt, placeholder: q.placeholder, field: q.frontMatterField, required: q.required };
8346
+ }
8347
+ function getWorkItemCaptureDefinition() {
8348
+ const questions = {};
8349
+ for (const type of WORK_ITEM_TYPES2) {
8350
+ questions[type] = getLevel(getLevelForType(type)).questions.map(toCapture);
8351
+ }
8352
+ return {
8353
+ types: WORK_ITEM_TYPES2.map((t) => ({ value: t, label: t.charAt(0).toUpperCase() + t.slice(1) })),
8354
+ questions
8355
+ };
8356
+ }
8357
+ function getWorkItemAgentAssets() {
8358
+ const agent = AGENT_PROMPTS.find((p2) => p2.fileName === "work-item-agent.md");
8359
+ const skill2 = skillById("work-item-refinement");
8360
+ return { agentPrompt: agent?.content ?? "", skill: skill2?.content ?? null };
8361
+ }
8362
+ function assembleRefinementContext(dir, workItemId) {
8363
+ const edit = getWorkItemForEdit(dir, workItemId);
8364
+ const config = loadConfig(dir);
8365
+ const knowledgeArtifacts = discoverKnowledge(dir).filter(
8366
+ (a) => !a.isWorkItem && a.type !== "skill" && a.type !== "agent" && a.layer !== "unknown"
8367
+ );
8368
+ const decisions = knowledgeArtifacts.filter((a) => a.type === "adr" || /^adr-/i.test(a.id)).map((a) => ({ id: a.id, title: a.title || a.id }));
8369
+ const knowledge = knowledgeArtifacts.filter((a) => !(a.type === "adr" || /^adr-/i.test(a.id))).map((a) => ({ id: a.id || a.relPath, title: a.title || a.id, layer: a.layer, type: a.type || void 0, summary: a.summary || void 0 }));
8370
+ const modules = ["core", ...loadMappedModules(dir).map((m) => m.id)].filter((v, i, arr) => arr.indexOf(v) === i);
8371
+ return {
8372
+ workItem: { id: edit.id, title: edit.title, status: edit.status, intent: edit.summary ?? edit.title, current: stripEdit(edit) },
8373
+ project: { name: config?.project.name ?? "unknown", state: config?.project.state ?? "unknown", structure: config?.project.structure ?? "unknown" },
8374
+ modules,
8375
+ knowledge,
8376
+ decisions,
8377
+ revision: edit.revision
8378
+ };
8379
+ }
8380
+ function stripEdit(edit) {
8381
+ const { id: _i, status: _s, revision: _r, path: _p, editable: _e, editableReason: _er, ...input } = edit;
8382
+ return input;
8383
+ }
8384
+ var VALID_COVERAGE2 = /* @__PURE__ */ new Set(["affected", "reviewed-not-affected", "unknown", "not-applicable"]);
8385
+ var VALID_CONFIDENCE2 = /* @__PURE__ */ new Set(["high", "medium", "low"]);
8386
+ function str(v) {
8387
+ return typeof v === "string" && v.trim() ? v.trim() : void 0;
8388
+ }
8389
+ function strList(v) {
8390
+ return Array.isArray(v) ? v.map((x) => typeof x === "string" ? x.trim() : "").filter(Boolean) : [];
8391
+ }
8392
+ function normalizeAndValidateProposal(dir, workItemId, proposal) {
8393
+ const ctx = assembleRefinementContext(dir, workItemId);
8394
+ const knownModules = new Set(ctx.modules);
8395
+ const knownDecisions = new Set(ctx.decisions.map((d) => d.id));
8396
+ const knownKnowledge = new Set(ctx.knowledge.map((k) => k.id));
8397
+ const extraFindings = [];
8398
+ const input = { ...ctx.workItem.current };
8399
+ if (str(proposal.title)) input.title = str(proposal.title);
8400
+ const o = proposal.outcome ?? {};
8401
+ if (str(o.actor) !== void 0) input.actor = str(o.actor);
8402
+ if (str(o.observableOutcome) !== void 0) input.outcome = str(o.observableOutcome);
8403
+ if (str(o.currentBehavior) !== void 0) input.currentBehavior = str(o.currentBehavior);
8404
+ if (str(o.targetBehavior) !== void 0) input.targetBehavior = str(o.targetBehavior);
8405
+ const j = proposal.journey ?? {};
8406
+ if (j.entryPoints) input.entryPoints = strList(j.entryPoints).join("\n");
8407
+ if (j.flow) input.endToEndFlow = strList(j.flow).map((s) => `- ${s}`).join("\n");
8408
+ if (proposal.moduleCoverage) {
8409
+ input.moduleCoverage = proposal.moduleCoverage.filter((c) => {
8410
+ if (!knownModules.has(c.id)) {
8411
+ extraFindings.push({ level: "warning", message: `Proposed module "${c.id}" is not registered and was not included.` });
8412
+ return false;
8413
+ }
8414
+ return VALID_COVERAGE2.has(c.status);
8415
+ }).map((c) => ({ id: c.id, status: c.status, ...str(c.reason) ? { reason: str(c.reason) } : {} }));
8416
+ }
8417
+ const affected = /* @__PURE__ */ new Set();
8418
+ for (const m of proposal.affectedModules ?? []) if (knownModules.has(m)) affected.add(m);
8419
+ for (const c of input.moduleCoverage) if (c.status === "affected") affected.add(c.id);
8420
+ if (proposal.affectedModules || proposal.moduleCoverage) input.affectedModules = [...affected];
8421
+ if (proposal.impactAnalysis) {
8422
+ input.impactAnalysis = proposal.impactAnalysis.filter((s) => VALID_COVERAGE2.has(s.status) && str(s.surface)).map((s) => ({ surface: str(s.surface), status: s.status, ...str(s.reason) ? { reason: str(s.reason) } : {}, ...str(s.question) ? { question: str(s.question) } : {} }));
8423
+ }
8424
+ if (proposal.scopeConfidence && VALID_CONFIDENCE2.has(proposal.scopeConfidence.level)) {
8425
+ input.scopeConfidence = { level: proposal.scopeConfidence.level, reasons: strList(proposal.scopeConfidence.reasons) };
8426
+ }
8427
+ if (proposal.scopeUnknowns) input.scopeUnknowns = strList(proposal.scopeUnknowns);
8428
+ if (proposal.acceptanceCriteria) input.acceptanceCriteria = strList(proposal.acceptanceCriteria).map((t) => ({ text: t, checked: null }));
8429
+ if (proposal.linkedDecisions) {
8430
+ input.decisions = proposal.linkedDecisions.filter((id) => {
8431
+ if (!knownDecisions.has(id)) {
8432
+ extraFindings.push({ level: "warning", message: `Proposed decision "${id}" does not exist and was not linked.` });
8433
+ return false;
8434
+ }
8435
+ return true;
8436
+ });
8437
+ }
8438
+ if (proposal.relatedKnowledge) {
8439
+ input.relatedKnowledge = proposal.relatedKnowledge.filter((id) => {
8440
+ if (!knownKnowledge.has(id)) {
8441
+ extraFindings.push({ level: "warning", message: `Proposed knowledge "${id}" could not be resolved and was not linked.` });
8442
+ return false;
8443
+ }
8444
+ return true;
8445
+ });
8446
+ }
8447
+ const findings = [...extraFindings, ...evaluate(input, knownModules)];
8448
+ const blocking = findings.filter((f) => f.level === "blocking").length;
8449
+ const warning = findings.filter((f) => f.level === "warning").length;
8450
+ const fyi = findings.filter((f) => f.level === "fyi").length;
8451
+ return { input, validation: { findings, blocking, warning, fyi, canApply: true } };
8452
+ }
8453
+ function evaluate(input, knownModules) {
8454
+ const findings = [];
8455
+ for (const c of input.moduleCoverage) {
8456
+ if (c.status === "affected" && !input.affectedModules.includes(c.id)) {
8457
+ findings.push({ level: "blocking", message: `${c.id} is marked affected in module coverage but is missing from affected_modules.` });
8458
+ }
8459
+ }
8460
+ for (const m of input.affectedModules) {
8461
+ if (!knownModules.has(m)) findings.push({ level: "blocking", message: `Module "${m}" is not registered in this project.` });
8462
+ }
8463
+ if (!input.targetBehavior?.trim()) findings.push({ level: "warning", message: "Target behavior is not defined." });
8464
+ if (input.acceptanceCriteria.length === 0) findings.push({ level: "warning", message: "No acceptance criteria have been defined." });
8465
+ if (input.scopeConfidence?.level === "low") findings.push({ level: "warning", message: "Scope confidence is Low." });
8466
+ if (!input.scopeConfidence) findings.push({ level: "warning", message: "Scope confidence has not been assessed." });
8467
+ for (const s of input.impactAnalysis) if (s.status === "unknown") findings.push({ level: "fyi", message: `Impact on ${s.surface} is unknown.` });
8468
+ return findings;
8469
+ }
8470
+ function applyRefinement(dir, workItemId, proposal, expectedRevision) {
8471
+ const { input } = normalizeAndValidateProposal(dir, workItemId, proposal);
8472
+ return updateWorkItem(dir, workItemId, input, expectedRevision);
8473
+ }
8128
8474
  export {
8129
8475
  WorkItemNotFoundError,
8130
8476
  WorkItemWriteError,
8131
8477
  analyzeCrossRepoEvidence,
8132
8478
  analyzeScopeCoverage,
8479
+ applyRefinement,
8480
+ assembleRefinementContext,
8133
8481
  buildProjectExplanation,
8134
8482
  buildProjectRoute,
8135
8483
  buildReadinessReport,
@@ -8139,6 +8487,8 @@ export {
8139
8487
  discoverWorkItems,
8140
8488
  exists,
8141
8489
  getWorkItem,
8490
+ getWorkItemAgentAssets,
8491
+ getWorkItemCaptureDefinition,
8142
8492
  getWorkItemForEdit,
8143
8493
  getWorkItems,
8144
8494
  getWorkItemsSummary,
@@ -8150,6 +8500,7 @@ export {
8150
8500
  lifecycleStateOf,
8151
8501
  loadConfig,
8152
8502
  loadMappedModules,
8503
+ normalizeAndValidateProposal,
8153
8504
  readFile,
8154
8505
  transitionWorkItem,
8155
8506
  updateWorkItem,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/cli",
3
- "version": "3.73.0",
3
+ "version": "3.74.1",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {