@kaddo/cli 3.73.0 → 3.74.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/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 = {
@@ -8004,6 +8007,28 @@ function getWorkItemForEdit(dir, id) {
8004
8007
  ...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
8008
  };
8006
8009
  }
8010
+ var CAPTURE_SECTIONS = [
8011
+ { field: "problem", heading: "Problem" },
8012
+ { field: "expected_result", heading: "Expected result" },
8013
+ { field: "impact", heading: "Impact" },
8014
+ { field: "acceptance_criteria", heading: "Acceptance criteria", list: true },
8015
+ { field: "design", heading: "Design" },
8016
+ { field: "risks", heading: "Risks" }
8017
+ ];
8018
+ function captureBody(title, type, answers) {
8019
+ const out = [`# ${title}`, "", `> Type: ${type}`];
8020
+ for (const { field, heading, list: list2 } of CAPTURE_SECTIONS) {
8021
+ const value = answers[field]?.trim();
8022
+ if (!value) continue;
8023
+ if (list2) {
8024
+ const items = value.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).map((l) => /^[-*+]\s/.test(l) ? l : `- ${l}`);
8025
+ out.push("", `## ${heading}`, "", items.join("\n"));
8026
+ } else {
8027
+ out.push("", `## ${heading}`, "", value);
8028
+ }
8029
+ }
8030
+ return out.join("\n") + "\n";
8031
+ }
8007
8032
  function createWorkItem(dir, opts) {
8008
8033
  const intent = opts.intent.trim();
8009
8034
  if (!intent) throw new WorkItemWriteError("INVALID_INPUT", "An intent or summary is required.");
@@ -8024,7 +8049,9 @@ function createWorkItem(dir, opts) {
8024
8049
  affected_modules: [],
8025
8050
  summary: intent
8026
8051
  };
8027
- const input = {
8052
+ const answers = opts.answers ?? {};
8053
+ const hasAnswers = Object.values(answers).some((v) => v?.trim());
8054
+ const body = hasAnswers ? captureBody(title, type, answers) : freshBody({
8028
8055
  title,
8029
8056
  type,
8030
8057
  summary: intent,
@@ -8036,8 +8063,7 @@ function createWorkItem(dir, opts) {
8036
8063
  decisions: [],
8037
8064
  relatedKnowledge: [],
8038
8065
  scopeConfidence: null
8039
- };
8040
- const body = freshBody(input);
8066
+ });
8041
8067
  const relPath = `${WORK_ITEMS_DIR}/draft/${id}-${slugify2(title)}.md`;
8042
8068
  const filePath = join(dir, relPath);
8043
8069
  if (exists(filePath)) throw new WorkItemWriteError("INVALID_INPUT", `Work Item file already exists: ${relPath}`);
@@ -8125,11 +8151,312 @@ function transitionWorkItem(dir, id, to, expectedRevision) {
8125
8151
  }
8126
8152
  return { revision: revisionOf(nextRaw), status: to, path: targetRel };
8127
8153
  }
8154
+
8155
+ // src/core/knowledge-levels.ts
8156
+ var WORK_ITEM_TYPES2 = ["feature", "bugfix", "hotfix", "spike", "chore"];
8157
+ var LEVELS = {
8158
+ K0: {
8159
+ level: "K0",
8160
+ description: "Trivial change. No formal knowledge required.",
8161
+ questions: [],
8162
+ qualityGate: []
8163
+ },
8164
+ K1: {
8165
+ level: "K1",
8166
+ description: "Simple fix or hotfix.",
8167
+ questions: [
8168
+ {
8169
+ id: "problem",
8170
+ prompt: "What problem does this fix?",
8171
+ placeholder: "e.g. Button is not clickable on mobile",
8172
+ frontMatterField: "problem",
8173
+ required: true
8174
+ },
8175
+ {
8176
+ id: "expected_result",
8177
+ prompt: "What is the expected result after the fix?",
8178
+ placeholder: "e.g. Button works correctly on all screen sizes",
8179
+ frontMatterField: "expected_result",
8180
+ required: true
8181
+ }
8182
+ ],
8183
+ qualityGate: ["Problem is clear.", "Expected result is defined."]
8184
+ },
8185
+ K2: {
8186
+ level: "K2",
8187
+ description: "Feature or bugfix with functional impact.",
8188
+ questions: [
8189
+ {
8190
+ id: "problem",
8191
+ prompt: "What problem does this solve?",
8192
+ placeholder: "e.g. Users cannot complete checkout without email verification",
8193
+ frontMatterField: "problem",
8194
+ required: true
8195
+ },
8196
+ {
8197
+ id: "expected_result",
8198
+ prompt: "What is the expected result?",
8199
+ placeholder: "e.g. Users can verify email inline during checkout",
8200
+ frontMatterField: "expected_result",
8201
+ required: true
8202
+ },
8203
+ {
8204
+ id: "impact",
8205
+ prompt: "What is the impact if this is not done?",
8206
+ placeholder: "e.g. ~30% of users drop off at checkout",
8207
+ frontMatterField: "impact",
8208
+ required: true
8209
+ },
8210
+ {
8211
+ id: "acceptance_criteria",
8212
+ prompt: "What are the acceptance criteria? (one per line, press Enter twice to finish)",
8213
+ placeholder: "e.g. Email is verified before payment step",
8214
+ frontMatterField: "acceptance_criteria",
8215
+ required: true
8216
+ }
8217
+ ],
8218
+ qualityGate: [
8219
+ "Problem is clear.",
8220
+ "Expected result is defined.",
8221
+ "Impact of not doing it is stated.",
8222
+ "Acceptance criteria are verifiable."
8223
+ ]
8224
+ },
8225
+ K3: {
8226
+ level: "K3",
8227
+ description: "Capability, integration, or significant functional change.",
8228
+ questions: [
8229
+ {
8230
+ id: "problem",
8231
+ prompt: "What problem does this solve?",
8232
+ placeholder: "e.g. Payments need to support multiple providers",
8233
+ frontMatterField: "problem",
8234
+ required: true
8235
+ },
8236
+ {
8237
+ id: "impact",
8238
+ prompt: "What is the impact if this is not done?",
8239
+ placeholder: "e.g. We are locked into a single provider with no fallback",
8240
+ frontMatterField: "impact",
8241
+ required: true
8242
+ },
8243
+ {
8244
+ id: "acceptance_criteria",
8245
+ prompt: "What are the acceptance criteria?",
8246
+ placeholder: "e.g. System routes to provider B when provider A fails",
8247
+ frontMatterField: "acceptance_criteria",
8248
+ required: true
8249
+ },
8250
+ {
8251
+ id: "design",
8252
+ prompt: "What is the proposed design or approach?",
8253
+ placeholder: "e.g. Abstract provider interface + strategy pattern",
8254
+ frontMatterField: "design",
8255
+ required: true
8256
+ }
8257
+ ],
8258
+ qualityGate: [
8259
+ "Problem is clear.",
8260
+ "Impact is stated.",
8261
+ "Acceptance criteria are verifiable.",
8262
+ "Design is sufficient to start."
8263
+ ]
8264
+ },
8265
+ K4: {
8266
+ level: "K4",
8267
+ description: "Architecture change, migration, or high-risk decision.",
8268
+ questions: [
8269
+ {
8270
+ id: "problem",
8271
+ prompt: "What problem does this solve?",
8272
+ placeholder: "e.g. Current auth system does not support SSO",
8273
+ frontMatterField: "problem",
8274
+ required: true
8275
+ },
8276
+ {
8277
+ id: "impact",
8278
+ prompt: "What is the impact if this is not done?",
8279
+ placeholder: "e.g. Enterprise clients cannot onboard",
8280
+ frontMatterField: "impact",
8281
+ required: true
8282
+ },
8283
+ {
8284
+ id: "design",
8285
+ prompt: "What is the proposed design or approach?",
8286
+ placeholder: "e.g. Replace JWT-only auth with OIDC + JWT hybrid",
8287
+ frontMatterField: "design",
8288
+ required: true
8289
+ },
8290
+ {
8291
+ id: "risks",
8292
+ prompt: "What are the main risks?",
8293
+ placeholder: "e.g. Existing sessions may be invalidated during migration",
8294
+ frontMatterField: "risks",
8295
+ required: true
8296
+ }
8297
+ ],
8298
+ qualityGate: [
8299
+ "Problem is clear.",
8300
+ "Impact is stated.",
8301
+ "Design is documented.",
8302
+ "Risks are identified.",
8303
+ "ADR is created or linked if applicable."
8304
+ ]
8305
+ }
8306
+ };
8307
+ var TYPE_TO_LEVEL = {
8308
+ hotfix: "K1",
8309
+ bugfix: "K2",
8310
+ feature: "K2",
8311
+ spike: "K3",
8312
+ // Chores are maintenance/config/tooling work — low ceremony (problem + expected result).
8313
+ chore: "K1"
8314
+ };
8315
+ function getLevel(level) {
8316
+ return LEVELS[level];
8317
+ }
8318
+ function getLevelForType(type) {
8319
+ return TYPE_TO_LEVEL[type];
8320
+ }
8321
+
8322
+ // src/core/work-item-refinement.ts
8323
+ function toCapture(q) {
8324
+ return { id: q.id, prompt: q.prompt, placeholder: q.placeholder, field: q.frontMatterField, required: q.required };
8325
+ }
8326
+ function getWorkItemCaptureDefinition() {
8327
+ const questions = {};
8328
+ for (const type of WORK_ITEM_TYPES2) {
8329
+ questions[type] = getLevel(getLevelForType(type)).questions.map(toCapture);
8330
+ }
8331
+ return {
8332
+ types: WORK_ITEM_TYPES2.map((t) => ({ value: t, label: t.charAt(0).toUpperCase() + t.slice(1) })),
8333
+ questions
8334
+ };
8335
+ }
8336
+ function getWorkItemAgentAssets() {
8337
+ const agent = AGENT_PROMPTS.find((p2) => p2.fileName === "work-item-agent.md");
8338
+ const skill2 = skillById("work-item-refinement");
8339
+ return { agentPrompt: agent?.content ?? "", skill: skill2?.content ?? null };
8340
+ }
8341
+ function assembleRefinementContext(dir, workItemId) {
8342
+ const edit = getWorkItemForEdit(dir, workItemId);
8343
+ const config = loadConfig(dir);
8344
+ const knowledgeArtifacts = discoverKnowledge(dir).filter(
8345
+ (a) => !a.isWorkItem && a.type !== "skill" && a.type !== "agent" && a.layer !== "unknown"
8346
+ );
8347
+ const decisions = knowledgeArtifacts.filter((a) => a.type === "adr" || /^adr-/i.test(a.id)).map((a) => ({ id: a.id, title: a.title || a.id }));
8348
+ 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 }));
8349
+ const modules = ["core", ...loadMappedModules(dir).map((m) => m.id)].filter((v, i, arr) => arr.indexOf(v) === i);
8350
+ return {
8351
+ workItem: { id: edit.id, title: edit.title, status: edit.status, intent: edit.summary ?? edit.title, current: stripEdit(edit) },
8352
+ project: { name: config?.project.name ?? "unknown", state: config?.project.state ?? "unknown", structure: config?.project.structure ?? "unknown" },
8353
+ modules,
8354
+ knowledge,
8355
+ decisions,
8356
+ revision: edit.revision
8357
+ };
8358
+ }
8359
+ function stripEdit(edit) {
8360
+ const { id: _i, status: _s, revision: _r, path: _p, editable: _e, editableReason: _er, ...input } = edit;
8361
+ return input;
8362
+ }
8363
+ var VALID_COVERAGE2 = /* @__PURE__ */ new Set(["affected", "reviewed-not-affected", "unknown", "not-applicable"]);
8364
+ var VALID_CONFIDENCE2 = /* @__PURE__ */ new Set(["high", "medium", "low"]);
8365
+ function str(v) {
8366
+ return typeof v === "string" && v.trim() ? v.trim() : void 0;
8367
+ }
8368
+ function strList(v) {
8369
+ return Array.isArray(v) ? v.map((x) => typeof x === "string" ? x.trim() : "").filter(Boolean) : [];
8370
+ }
8371
+ function normalizeAndValidateProposal(dir, workItemId, proposal) {
8372
+ const ctx = assembleRefinementContext(dir, workItemId);
8373
+ const knownModules = new Set(ctx.modules);
8374
+ const knownDecisions = new Set(ctx.decisions.map((d) => d.id));
8375
+ const knownKnowledge = new Set(ctx.knowledge.map((k) => k.id));
8376
+ const extraFindings = [];
8377
+ const input = { ...ctx.workItem.current };
8378
+ if (str(proposal.title)) input.title = str(proposal.title);
8379
+ const o = proposal.outcome ?? {};
8380
+ if (str(o.actor) !== void 0) input.actor = str(o.actor);
8381
+ if (str(o.observableOutcome) !== void 0) input.outcome = str(o.observableOutcome);
8382
+ if (str(o.currentBehavior) !== void 0) input.currentBehavior = str(o.currentBehavior);
8383
+ if (str(o.targetBehavior) !== void 0) input.targetBehavior = str(o.targetBehavior);
8384
+ const j = proposal.journey ?? {};
8385
+ if (j.entryPoints) input.entryPoints = strList(j.entryPoints).join("\n");
8386
+ if (j.flow) input.endToEndFlow = strList(j.flow).map((s) => `- ${s}`).join("\n");
8387
+ if (proposal.moduleCoverage) {
8388
+ input.moduleCoverage = proposal.moduleCoverage.filter((c) => {
8389
+ if (!knownModules.has(c.id)) {
8390
+ extraFindings.push({ level: "warning", message: `Proposed module "${c.id}" is not registered and was not included.` });
8391
+ return false;
8392
+ }
8393
+ return VALID_COVERAGE2.has(c.status);
8394
+ }).map((c) => ({ id: c.id, status: c.status, ...str(c.reason) ? { reason: str(c.reason) } : {} }));
8395
+ }
8396
+ const affected = /* @__PURE__ */ new Set();
8397
+ for (const m of proposal.affectedModules ?? []) if (knownModules.has(m)) affected.add(m);
8398
+ for (const c of input.moduleCoverage) if (c.status === "affected") affected.add(c.id);
8399
+ if (proposal.affectedModules || proposal.moduleCoverage) input.affectedModules = [...affected];
8400
+ if (proposal.impactAnalysis) {
8401
+ 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) } : {} }));
8402
+ }
8403
+ if (proposal.scopeConfidence && VALID_CONFIDENCE2.has(proposal.scopeConfidence.level)) {
8404
+ input.scopeConfidence = { level: proposal.scopeConfidence.level, reasons: strList(proposal.scopeConfidence.reasons) };
8405
+ }
8406
+ if (proposal.scopeUnknowns) input.scopeUnknowns = strList(proposal.scopeUnknowns);
8407
+ if (proposal.acceptanceCriteria) input.acceptanceCriteria = strList(proposal.acceptanceCriteria).map((t) => ({ text: t, checked: null }));
8408
+ if (proposal.linkedDecisions) {
8409
+ input.decisions = proposal.linkedDecisions.filter((id) => {
8410
+ if (!knownDecisions.has(id)) {
8411
+ extraFindings.push({ level: "warning", message: `Proposed decision "${id}" does not exist and was not linked.` });
8412
+ return false;
8413
+ }
8414
+ return true;
8415
+ });
8416
+ }
8417
+ if (proposal.relatedKnowledge) {
8418
+ input.relatedKnowledge = proposal.relatedKnowledge.filter((id) => {
8419
+ if (!knownKnowledge.has(id)) {
8420
+ extraFindings.push({ level: "warning", message: `Proposed knowledge "${id}" could not be resolved and was not linked.` });
8421
+ return false;
8422
+ }
8423
+ return true;
8424
+ });
8425
+ }
8426
+ const findings = [...extraFindings, ...evaluate(input, knownModules)];
8427
+ const blocking = findings.filter((f) => f.level === "blocking").length;
8428
+ const warning = findings.filter((f) => f.level === "warning").length;
8429
+ const fyi = findings.filter((f) => f.level === "fyi").length;
8430
+ return { input, validation: { findings, blocking, warning, fyi, canApply: true } };
8431
+ }
8432
+ function evaluate(input, knownModules) {
8433
+ const findings = [];
8434
+ for (const c of input.moduleCoverage) {
8435
+ if (c.status === "affected" && !input.affectedModules.includes(c.id)) {
8436
+ findings.push({ level: "blocking", message: `${c.id} is marked affected in module coverage but is missing from affected_modules.` });
8437
+ }
8438
+ }
8439
+ for (const m of input.affectedModules) {
8440
+ if (!knownModules.has(m)) findings.push({ level: "blocking", message: `Module "${m}" is not registered in this project.` });
8441
+ }
8442
+ if (!input.targetBehavior?.trim()) findings.push({ level: "warning", message: "Target behavior is not defined." });
8443
+ if (input.acceptanceCriteria.length === 0) findings.push({ level: "warning", message: "No acceptance criteria have been defined." });
8444
+ if (input.scopeConfidence?.level === "low") findings.push({ level: "warning", message: "Scope confidence is Low." });
8445
+ if (!input.scopeConfidence) findings.push({ level: "warning", message: "Scope confidence has not been assessed." });
8446
+ for (const s of input.impactAnalysis) if (s.status === "unknown") findings.push({ level: "fyi", message: `Impact on ${s.surface} is unknown.` });
8447
+ return findings;
8448
+ }
8449
+ function applyRefinement(dir, workItemId, proposal, expectedRevision) {
8450
+ const { input } = normalizeAndValidateProposal(dir, workItemId, proposal);
8451
+ return updateWorkItem(dir, workItemId, input, expectedRevision);
8452
+ }
8128
8453
  export {
8129
8454
  WorkItemNotFoundError,
8130
8455
  WorkItemWriteError,
8131
8456
  analyzeCrossRepoEvidence,
8132
8457
  analyzeScopeCoverage,
8458
+ applyRefinement,
8459
+ assembleRefinementContext,
8133
8460
  buildProjectExplanation,
8134
8461
  buildProjectRoute,
8135
8462
  buildReadinessReport,
@@ -8139,6 +8466,8 @@ export {
8139
8466
  discoverWorkItems,
8140
8467
  exists,
8141
8468
  getWorkItem,
8469
+ getWorkItemAgentAssets,
8470
+ getWorkItemCaptureDefinition,
8142
8471
  getWorkItemForEdit,
8143
8472
  getWorkItems,
8144
8473
  getWorkItemsSummary,
@@ -8150,6 +8479,7 @@ export {
8150
8479
  lifecycleStateOf,
8151
8480
  loadConfig,
8152
8481
  loadMappedModules,
8482
+ normalizeAndValidateProposal,
8153
8483
  readFile,
8154
8484
  transitionWorkItem,
8155
8485
  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.0",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {