@kaddo/cli 3.72.2 → 3.73.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
@@ -116,8 +116,8 @@ function loadConfig(dir) {
116
116
  const parsed = configSchema.safeParse(raw ?? {});
117
117
  if (!parsed.success) {
118
118
  const issues = parsed.error.issues.map((i) => {
119
- const path2 = i.path.join(".");
120
- return path2 ? ` - ${path2}: ${i.message}` : ` - ${i.message}`;
119
+ const path3 = i.path.join(".");
120
+ return path3 ? ` - ${path3}: ${i.message}` : ` - ${i.message}`;
121
121
  }).join("\n");
122
122
  throw new ConfigError(`Invalid .kaddo/config.yml:
123
123
  ${issues}`);
@@ -352,11 +352,11 @@ function moduleArtifactCoverage(dir, id) {
352
352
  };
353
353
  }
354
354
  function loadMappedModules(dir) {
355
- const path2 = join(dir, DESCRIPTOR_PATH);
356
- if (!exists(path2)) return [];
355
+ const path3 = join(dir, DESCRIPTOR_PATH);
356
+ if (!exists(path3)) return [];
357
357
  let parsed;
358
358
  try {
359
- parsed = parseYaml3(readFile(path2));
359
+ parsed = parseYaml3(readFile(path3));
360
360
  } catch {
361
361
  return [];
362
362
  }
@@ -1590,7 +1590,7 @@ function sectionParagraph(md, title) {
1590
1590
  }
1591
1591
  return "";
1592
1592
  }
1593
- function parseCapsule(id, path2, md) {
1593
+ function parseCapsule(id, path3, md) {
1594
1594
  const { data } = matter2(md);
1595
1595
  const updatedAt = data.updated_at ? String(data.updated_at) : void 0;
1596
1596
  let ageDays = null;
@@ -1600,7 +1600,7 @@ function parseCapsule(id, path2, md) {
1600
1600
  }
1601
1601
  return {
1602
1602
  id,
1603
- path: path2,
1603
+ path: path3,
1604
1604
  system: data.system ? String(data.system) : id,
1605
1605
  owner: data.owner ? String(data.owner) : void 0,
1606
1606
  updatedAt,
@@ -2761,10 +2761,10 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
2761
2761
  const q = (rel) => analyzeKnowledgeArtifact(dir, rel);
2762
2762
  const resolveAgent = (agent) => {
2763
2763
  const file = agent.endsWith(".md") ? agent : `${agent}.md`;
2764
- const path2 = agentInstallPath(file);
2765
- const installed = isFile(join(dir, path2));
2764
+ const path3 = agentInstallPath(file);
2765
+ const installed = isFile(join(dir, path3));
2766
2766
  const group = agentGroupOf(file);
2767
- return { agentPath: path2, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
2767
+ return { agentPath: path3, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
2768
2768
  };
2769
2769
  const moduleRepo = isModule(config);
2770
2770
  const coreRepo = isCore(config);
@@ -7715,18 +7715,431 @@ function parseRelatedKnowledge(fm, knowledgeById) {
7715
7715
  }
7716
7716
  return out;
7717
7717
  }
7718
+
7719
+ // src/core/work-item-write.ts
7720
+ import fs2 from "fs";
7721
+ import path2 from "path";
7722
+ import crypto from "crypto";
7723
+ import matter7 from "gray-matter";
7724
+ var WORK_ITEMS_DIR = "knowledge/delivery/work-items";
7725
+ var WorkItemWriteError = class extends Error {
7726
+ constructor(code, message) {
7727
+ super(message);
7728
+ this.code = code;
7729
+ this.name = "WorkItemWriteError";
7730
+ }
7731
+ code;
7732
+ };
7733
+ var VALID_COVERAGE = /* @__PURE__ */ new Set(["affected", "reviewed-not-affected", "unknown", "not-applicable"]);
7734
+ var VALID_CONFIDENCE = /* @__PURE__ */ new Set(["high", "medium", "low"]);
7735
+ var EDITABLE_STATES = ["draft"];
7736
+ function revisionOf(raw) {
7737
+ return crypto.createHash("sha256").update(raw, "utf-8").digest("hex");
7738
+ }
7739
+ function slugify2(s) {
7740
+ return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 50);
7741
+ }
7742
+ function nextWorkItemId(dir) {
7743
+ const wiDir = join(dir, WORK_ITEMS_DIR);
7744
+ let max = 0;
7745
+ const walk = (d) => {
7746
+ if (!exists(d)) return;
7747
+ for (const entry of fs2.readdirSync(d)) {
7748
+ const full = join(d, entry);
7749
+ if (isFile(full)) {
7750
+ const m = entry.match(/WI-(\d+)/);
7751
+ if (m) max = Math.max(max, parseInt(m[1], 10));
7752
+ } else if (!entry.startsWith(".")) {
7753
+ walk(full);
7754
+ }
7755
+ }
7756
+ };
7757
+ walk(wiDir);
7758
+ return `WI-${String(max + 1).padStart(3, "0")}`;
7759
+ }
7760
+ function atomicWrite(filePath, content) {
7761
+ fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
7762
+ const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
7763
+ fs2.writeFileSync(tmp, content, "utf-8");
7764
+ try {
7765
+ fs2.renameSync(tmp, filePath);
7766
+ } catch (err) {
7767
+ try {
7768
+ fs2.rmSync(tmp, { force: true });
7769
+ } catch {
7770
+ }
7771
+ throw err;
7772
+ }
7773
+ }
7774
+ function findArtifact(dir, id) {
7775
+ const match = discoverWorkItems(dir).find((a) => (a.id || a.title) === id);
7776
+ if (!match) throw new WorkItemWriteError("WORK_ITEM_NOT_FOUND", `Work Item "${id}" was not found.`);
7777
+ return { filePath: match.filePath, relPath: match.relPath };
7778
+ }
7779
+ function validModuleIds(dir) {
7780
+ const ids = /* @__PURE__ */ new Set(["core"]);
7781
+ for (const m of loadMappedModules(dir)) ids.add(m.id);
7782
+ return ids;
7783
+ }
7784
+ var SECTION_ORDER = [
7785
+ "Actor",
7786
+ "Outcome",
7787
+ "Current behavior",
7788
+ "Target behavior",
7789
+ "Entry points",
7790
+ "End-to-end flow",
7791
+ "Scope unknowns",
7792
+ "Acceptance criteria"
7793
+ ];
7794
+ var KNOWN_HEADINGS = new Set(SECTION_ORDER.map((s) => s.toLowerCase()));
7795
+ function parseBody(content) {
7796
+ const lines = content.split(/\r?\n/);
7797
+ const preamble = [];
7798
+ const sections = [];
7799
+ let current = null;
7800
+ let buffer = [];
7801
+ let seenHeading = false;
7802
+ const flush = () => {
7803
+ if (current) {
7804
+ current.body = buffer.join("\n").trim();
7805
+ sections.push(current);
7806
+ }
7807
+ buffer = [];
7808
+ };
7809
+ for (const line of lines) {
7810
+ const m = line.match(/^##\s+(.*?)\s*$/);
7811
+ if (m) {
7812
+ seenHeading = true;
7813
+ flush();
7814
+ current = { heading: m[1].trim(), normalized: m[1].trim().toLowerCase(), body: "" };
7815
+ } else if (!seenHeading) {
7816
+ preamble.push(line);
7817
+ } else {
7818
+ buffer.push(line);
7819
+ }
7820
+ }
7821
+ flush();
7822
+ return { preamble, sections };
7823
+ }
7824
+ function renderList(items) {
7825
+ return items.map((i) => `- ${i.trim()}`).filter((l) => l.trim() !== "-").join("\n");
7826
+ }
7827
+ function renderCriteria(items) {
7828
+ return items.filter((c) => c.text.trim()).map((c) => c.checked === true ? `- [x] ${c.text.trim()}` : c.checked === false ? `- [ ] ${c.text.trim()}` : `- ${c.text.trim()}`).join("\n");
7829
+ }
7830
+ function desiredSections(input) {
7831
+ const d = /* @__PURE__ */ new Map();
7832
+ const put = (heading, value) => d.set(heading.toLowerCase(), value.trim() ? value.trim() : null);
7833
+ put("Actor", input.actor ?? "");
7834
+ put("Outcome", input.outcome ?? "");
7835
+ put("Current behavior", input.currentBehavior ?? "");
7836
+ put("Target behavior", input.targetBehavior ?? "");
7837
+ put("Entry points", input.entryPoints ?? "");
7838
+ put("End-to-end flow", input.endToEndFlow ?? "");
7839
+ d.set("scope unknowns", input.scopeUnknowns.some((u) => u.trim()) ? renderList(input.scopeUnknowns) : null);
7840
+ d.set("acceptance criteria", input.acceptanceCriteria.some((c) => c.text.trim()) ? renderCriteria(input.acceptanceCriteria) : null);
7841
+ return d;
7842
+ }
7843
+ function headingFor(normalized) {
7844
+ return SECTION_ORDER.find((s) => s.toLowerCase() === normalized) ?? normalized;
7845
+ }
7846
+ function mergeBody(existing, input) {
7847
+ const { preamble, sections } = parseBody(existing);
7848
+ const desired = desiredSections(input);
7849
+ const handled = /* @__PURE__ */ new Set();
7850
+ const preambleLines = [...preamble];
7851
+ const h1Index = preambleLines.findIndex((l) => /^#\s+/.test(l));
7852
+ if (h1Index >= 0) preambleLines[h1Index] = `# ${input.title}`;
7853
+ const out = [];
7854
+ for (const s of sections) {
7855
+ if (KNOWN_HEADINGS.has(s.normalized)) {
7856
+ handled.add(s.normalized);
7857
+ const body = desired.get(s.normalized);
7858
+ if (body != null) out.push(`## ${headingFor(s.normalized)}
7859
+
7860
+ ${body}`);
7861
+ } else {
7862
+ out.push(`## ${s.heading}${s.body ? `
7863
+
7864
+ ${s.body}` : ""}`);
7865
+ }
7866
+ }
7867
+ for (const heading of SECTION_ORDER) {
7868
+ const norm = heading.toLowerCase();
7869
+ if (handled.has(norm)) continue;
7870
+ const body = desired.get(norm);
7871
+ if (body != null) out.push(`## ${heading}
7872
+
7873
+ ${body}`);
7874
+ }
7875
+ const preambleText = preambleLines.join("\n").trim();
7876
+ return `${preambleText}
7877
+
7878
+ ${out.join("\n\n")}
7879
+ `;
7880
+ }
7881
+ function freshBody(input) {
7882
+ const preamble = `# ${input.title}
7883
+
7884
+ > Type: ${input.type}`;
7885
+ const out = [];
7886
+ const desired = desiredSections(input);
7887
+ for (const heading of SECTION_ORDER) {
7888
+ const body = desired.get(heading.toLowerCase());
7889
+ if (body != null) out.push(`## ${heading}
7890
+
7891
+ ${body}`);
7892
+ }
7893
+ return `${preamble}
7894
+
7895
+ ${out.join("\n\n")}
7896
+ `.replace(/\n{3,}/g, "\n\n");
7897
+ }
7898
+ function applyFrontmatter(data, input) {
7899
+ const next = { ...data };
7900
+ next.title = input.title;
7901
+ next.type = input.type;
7902
+ next.work_type = input.type;
7903
+ if (input.summary != null) next.summary = input.summary;
7904
+ next.affected_modules = [...input.affectedModules];
7905
+ if (input.scopeConfidence && VALID_CONFIDENCE.has(input.scopeConfidence.level)) {
7906
+ next.scope_confidence = { level: input.scopeConfidence.level, reasons: input.scopeConfidence.reasons.filter((r) => r.trim()) };
7907
+ } else {
7908
+ delete next.scope_confidence;
7909
+ }
7910
+ const coverage = {};
7911
+ for (const c of input.moduleCoverage) {
7912
+ if (!VALID_COVERAGE.has(c.status)) continue;
7913
+ coverage[c.id] = c.reason?.trim() ? { status: c.status, reason: c.reason.trim() } : { status: c.status };
7914
+ }
7915
+ if (Object.keys(coverage).length > 0) next.module_coverage = coverage;
7916
+ else delete next.module_coverage;
7917
+ const surfaces = {};
7918
+ for (const s of input.impactAnalysis) {
7919
+ if (!VALID_COVERAGE.has(s.status)) continue;
7920
+ const entry = { status: s.status };
7921
+ if (s.reason?.trim()) entry.reason = s.reason.trim();
7922
+ if (s.question?.trim()) entry.question = s.question.trim();
7923
+ surfaces[s.surface] = entry;
7924
+ }
7925
+ if (Object.keys(surfaces).length > 0) next.impact_analysis = { surfaces };
7926
+ else delete next.impact_analysis;
7927
+ if (input.decisions.length > 0) next.decisions = [...input.decisions];
7928
+ else delete next.decisions;
7929
+ if (input.relatedKnowledge.length > 0) next.related_knowledge = [...input.relatedKnowledge];
7930
+ else delete next.related_knowledge;
7931
+ return next;
7932
+ }
7933
+ function serialize(data, body) {
7934
+ return matter7.stringify(`
7935
+ ${body.trim()}
7936
+ `, data);
7937
+ }
7938
+ function toInput(data, content) {
7939
+ const { sections } = parseBody(content);
7940
+ const sec = (name) => {
7941
+ const s = sections.find((x) => x.normalized === name);
7942
+ return s && s.body.trim() ? s.body.trim() : void 0;
7943
+ };
7944
+ const bullets = (name) => {
7945
+ const body = sec(name);
7946
+ if (!body) return [];
7947
+ return body.split(/\r?\n/).map((l) => l.replace(/^\s*[-*+]\s+(\[[ xX]\]\s*)?/, "").trim()).filter(Boolean);
7948
+ };
7949
+ const criteria = (() => {
7950
+ const body = sec("acceptance criteria");
7951
+ if (!body) return [];
7952
+ const out = [];
7953
+ for (const line of body.split(/\r?\n/)) {
7954
+ const m = line.match(/^\s*[-*+]\s+(.*)$/);
7955
+ if (!m) continue;
7956
+ let text3 = m[1].trim();
7957
+ let checked = null;
7958
+ const cb = text3.match(/^\[([ xX])\]\s*(.*)$/);
7959
+ if (cb) {
7960
+ checked = cb[1].toLowerCase() === "x";
7961
+ text3 = cb[2].trim();
7962
+ }
7963
+ if (text3) out.push({ text: text3, checked });
7964
+ }
7965
+ return out;
7966
+ })();
7967
+ const sc = data.scope_confidence;
7968
+ const mc = data.module_coverage;
7969
+ const ia = data.impact_analysis?.surfaces;
7970
+ return {
7971
+ title: String(data.title ?? ""),
7972
+ type: String(data.type ?? "feature"),
7973
+ summary: data.summary ? String(data.summary) : void 0,
7974
+ actor: sec("actor"),
7975
+ outcome: sec("outcome"),
7976
+ currentBehavior: sec("current behavior"),
7977
+ targetBehavior: sec("target behavior"),
7978
+ entryPoints: sec("entry points"),
7979
+ endToEndFlow: sec("end-to-end flow"),
7980
+ scopeConfidence: sc && sc.level ? { level: String(sc.level), reasons: Array.isArray(sc.reasons) ? sc.reasons.map(String) : [] } : null,
7981
+ scopeUnknowns: bullets("scope unknowns"),
7982
+ affectedModules: Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [],
7983
+ moduleCoverage: mc ? Object.entries(mc).map(([id, v]) => ({ id, status: String(v.status ?? ""), ...v.reason ? { reason: String(v.reason) } : {} })) : [],
7984
+ impactAnalysis: ia ? Object.entries(ia).map(([surface, v]) => ({ surface, status: String(v.status ?? ""), ...v.reason ? { reason: String(v.reason) } : {}, ...v.question ? { question: String(v.question) } : {} })) : [],
7985
+ acceptanceCriteria: criteria,
7986
+ decisions: Array.isArray(data.decisions) ? data.decisions.map(String) : [],
7987
+ relatedKnowledge: Array.isArray(data.related_knowledge) ? data.related_knowledge.map(String) : []
7988
+ };
7989
+ }
7990
+ function getWorkItemForEdit(dir, id) {
7991
+ const { filePath, relPath } = findArtifact(dir, id);
7992
+ const raw = readFile(filePath);
7993
+ const { data, content } = matter7(raw);
7994
+ const status = lifecycleStateOf({ status: String(data.status ?? ""), filePath });
7995
+ const input = toInput(data, content);
7996
+ const editable = EDITABLE_STATES.includes(status);
7997
+ return {
7998
+ ...input,
7999
+ id,
8000
+ status,
8001
+ revision: revisionOf(raw),
8002
+ path: relPath,
8003
+ editable,
8004
+ ...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
+ };
8006
+ }
8007
+ function createWorkItem(dir, opts) {
8008
+ const intent = opts.intent.trim();
8009
+ 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}".`);
8012
+ const id = nextWorkItemId(dir);
8013
+ const title = intent.split(/\r?\n/)[0].trim().slice(0, 120);
8014
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
8015
+ const data = {
8016
+ type,
8017
+ id,
8018
+ title,
8019
+ status: "draft",
8020
+ work_type: type,
8021
+ created_at: today,
8022
+ source: { type: "manual", inferred: false },
8023
+ generated_by: "kaddo-admin",
8024
+ affected_modules: [],
8025
+ summary: intent
8026
+ };
8027
+ const input = {
8028
+ title,
8029
+ type,
8030
+ summary: intent,
8031
+ scopeUnknowns: [],
8032
+ affectedModules: [],
8033
+ moduleCoverage: [],
8034
+ impactAnalysis: [],
8035
+ acceptanceCriteria: [],
8036
+ decisions: [],
8037
+ relatedKnowledge: [],
8038
+ scopeConfidence: null
8039
+ };
8040
+ const body = freshBody(input);
8041
+ const relPath = `${WORK_ITEMS_DIR}/draft/${id}-${slugify2(title)}.md`;
8042
+ const filePath = join(dir, relPath);
8043
+ if (exists(filePath)) throw new WorkItemWriteError("INVALID_INPUT", `Work Item file already exists: ${relPath}`);
8044
+ const raw = serialize(data, body);
8045
+ atomicWrite(filePath, raw);
8046
+ return { id, path: relPath, revision: revisionOf(raw) };
8047
+ }
8048
+ function validateInput(input) {
8049
+ 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}".`);
8051
+ }
8052
+ function updateWorkItem(dir, id, input, expectedRevision) {
8053
+ validateInput(input);
8054
+ const { filePath, relPath } = findArtifact(dir, id);
8055
+ const raw = readFile(filePath);
8056
+ if (revisionOf(raw) !== expectedRevision) {
8057
+ throw new WorkItemWriteError("WORK_ITEM_CONFLICT", "This Work Item changed outside Kaddo Admin. Reload the latest version before saving.");
8058
+ }
8059
+ const { data, content } = matter7(raw);
8060
+ const status = lifecycleStateOf({ status: String(data.status ?? ""), filePath });
8061
+ if (!EDITABLE_STATES.includes(status)) {
8062
+ throw new WorkItemWriteError("WORK_ITEM_NOT_EDITABLE", `A ${status} Work Item cannot be edited. Reopen it as Draft first.`);
8063
+ }
8064
+ const nextData = applyFrontmatter(data, input);
8065
+ const nextBody = mergeBody(content, input);
8066
+ const nextRaw = serialize(nextData, nextBody);
8067
+ atomicWrite(filePath, nextRaw);
8068
+ return { revision: revisionOf(nextRaw), path: relPath };
8069
+ }
8070
+ function validateWorkItem(dir, id) {
8071
+ const { filePath } = findArtifact(dir, id);
8072
+ const raw = readFile(filePath);
8073
+ const { data, content } = matter7(raw);
8074
+ const input = toInput(data, content);
8075
+ const findings = [];
8076
+ const modules = validModuleIds(dir);
8077
+ for (const c of input.moduleCoverage) {
8078
+ if (c.status === "affected" && !input.affectedModules.includes(c.id)) {
8079
+ findings.push({ level: "blocking", message: `${c.id} is marked affected in module coverage but is missing from affected_modules.` });
8080
+ }
8081
+ }
8082
+ for (const m of input.affectedModules) {
8083
+ if (!modules.has(m)) findings.push({ level: "blocking", message: `Module "${m}" is not registered in this project.` });
8084
+ }
8085
+ if (!input.targetBehavior?.trim()) findings.push({ level: "warning", message: "Target behavior is not defined." });
8086
+ if (input.acceptanceCriteria.length === 0) findings.push({ level: "warning", message: "No acceptance criteria have been defined." });
8087
+ if (input.scopeConfidence?.level === "low") findings.push({ level: "warning", message: "Scope confidence is Low." });
8088
+ if (!input.scopeConfidence) findings.push({ level: "warning", message: "Scope confidence has not been assessed." });
8089
+ for (const c of input.moduleCoverage) {
8090
+ if (c.status === "reviewed-not-affected") findings.push({ level: "fyi", message: `${c.id} was reviewed and is not affected.` });
8091
+ }
8092
+ for (const s of input.impactAnalysis) {
8093
+ if (s.status === "unknown") findings.push({ level: "fyi", message: `Impact on ${s.surface} is unknown.` });
8094
+ }
8095
+ const canMarkReady = !findings.some((f) => f.level === "blocking");
8096
+ return { findings, canMarkReady };
8097
+ }
8098
+ var ALLOWED = { draft: ["ready"], ready: ["draft"] };
8099
+ function transitionWorkItem(dir, id, to, expectedRevision) {
8100
+ const { filePath } = findArtifact(dir, id);
8101
+ const raw = readFile(filePath);
8102
+ if (revisionOf(raw) !== expectedRevision) {
8103
+ throw new WorkItemWriteError("WORK_ITEM_CONFLICT", "This Work Item changed outside Kaddo Admin. Reload the latest version before continuing.");
8104
+ }
8105
+ const { data, content } = matter7(raw);
8106
+ const from = lifecycleStateOf({ status: String(data.status ?? ""), filePath });
8107
+ if (!(ALLOWED[from] ?? []).includes(to)) {
8108
+ throw new WorkItemWriteError("INVALID_TRANSITION", `Cannot transition a ${from} Work Item to ${to}.`);
8109
+ }
8110
+ if (to === "ready") {
8111
+ const { canMarkReady } = validateWorkItem(dir, id);
8112
+ if (!canMarkReady) throw new WorkItemWriteError("INVALID_TRANSITION", "This Work Item has blocking issues and cannot be marked Ready.");
8113
+ }
8114
+ const nextData = { ...data, status: to };
8115
+ const nextRaw = serialize(nextData, content);
8116
+ const filename = path2.basename(filePath);
8117
+ const targetRel = `${WORK_ITEMS_DIR}/${to}/${filename}`;
8118
+ const targetPath = join(dir, targetRel);
8119
+ atomicWrite(targetPath, nextRaw);
8120
+ if (path2.resolve(targetPath) !== path2.resolve(filePath)) {
8121
+ try {
8122
+ fs2.rmSync(filePath, { force: true });
8123
+ } catch {
8124
+ }
8125
+ }
8126
+ return { revision: revisionOf(nextRaw), status: to, path: targetRel };
8127
+ }
7718
8128
  export {
7719
8129
  WorkItemNotFoundError,
8130
+ WorkItemWriteError,
7720
8131
  analyzeCrossRepoEvidence,
7721
8132
  analyzeScopeCoverage,
7722
8133
  buildProjectExplanation,
7723
8134
  buildProjectRoute,
7724
8135
  buildReadinessReport,
8136
+ createWorkItem,
7725
8137
  cwd,
7726
8138
  discoverKnowledge,
7727
8139
  discoverWorkItems,
7728
8140
  exists,
7729
8141
  getWorkItem,
8142
+ getWorkItemForEdit,
7730
8143
  getWorkItems,
7731
8144
  getWorkItemsSummary,
7732
8145
  isActiveState,
@@ -7737,5 +8150,8 @@ export {
7737
8150
  lifecycleStateOf,
7738
8151
  loadConfig,
7739
8152
  loadMappedModules,
7740
- readFile
8153
+ readFile,
8154
+ transitionWorkItem,
8155
+ updateWorkItem,
8156
+ validateWorkItem
7741
8157
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/cli",
3
- "version": "3.72.2",
3
+ "version": "3.73.0",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,2 +0,0 @@
1
- /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.collapse{visibility:collapse}.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.border{border-style:var(--tw-border-style);border-width:1px}.font-mono{font-family:var(--font-mono)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}}:root{--background:#fff;--surface:#f8f9fa;--surface-muted:#f1f3f5;--foreground:#1a1a2e;--foreground-muted:#6c757d;--border:#dee2e6;--border-strong:#adb5bd;--primary:#4361ee;--primary-foreground:#fff;--success:#2d9f5c;--warning:#e9a820;--danger:#dc3545;--info:#3b82f6;--finding-blocking:#dc3545;--finding-warning:#e9a820;--finding-fyi:#6c757d;--work-item-draft:#6c757d;--work-item-ready:#3b82f6;--work-item-progress:#8b5cf6;--work-item-blocked:#dc3545;--work-item-completed:#2d9f5c;--work-item-archived:#adb5bd;--knowledge-ready:#2d9f5c;--knowledge-missing:#dc3545;--knowledge-placeholder:#e9a820;--knowledge-unknown:#6c757d;--module-core:#4361ee;--module-module:#3b82f6;--module-unavailable:#dc3545;--readiness-ready:#2d9f5c;--readiness-warning:#e9a820;--readiness-blocked:#dc3545;--font-sans:"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono:"JetBrains Mono", "Fira Code", "Cascadia Code", monospace;--radius:6px}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--background:#0f0f23;--surface:#1a1a2e;--surface-muted:#16213e;--foreground:#e8e8e8;--foreground-muted:#a0a0b0;--border:#2a2a3e;--border-strong:#4a4a5e;--primary:#6580f5;--primary-foreground:#fff;--success:#3cb371;--warning:#f0b840;--danger:#ef4444;--info:#60a5fa;--finding-blocking:#ef4444;--finding-warning:#f0b840;--finding-fyi:#a0a0b0;--work-item-draft:#a0a0b0;--work-item-ready:#60a5fa;--work-item-progress:#a78bfa;--work-item-blocked:#ef4444;--work-item-completed:#3cb371;--work-item-archived:#6a6a7e;--knowledge-ready:#3cb371;--knowledge-missing:#ef4444;--knowledge-placeholder:#f0b840;--knowledge-unknown:#a0a0b0;--module-core:#6580f5;--module-module:#60a5fa;--module-unavailable:#ef4444;--readiness-ready:#3cb371;--readiness-warning:#f0b840;--readiness-blocked:#ef4444}}:root[data-theme=dark]{--background:#0f0f23;--surface:#1a1a2e;--surface-muted:#16213e;--foreground:#e8e8e8;--foreground-muted:#a0a0b0;--border:#2a2a3e;--border-strong:#4a4a5e;--primary:#6580f5;--primary-foreground:#fff;--success:#3cb371;--warning:#f0b840;--danger:#ef4444;--info:#60a5fa;--finding-blocking:#ef4444;--finding-warning:#f0b840;--finding-fyi:#a0a0b0;--work-item-draft:#a0a0b0;--work-item-ready:#60a5fa;--work-item-progress:#a78bfa;--work-item-blocked:#ef4444;--work-item-completed:#3cb371;--work-item-archived:#6a6a7e;--knowledge-ready:#3cb371;--knowledge-missing:#ef4444;--knowledge-placeholder:#f0b840;--knowledge-unknown:#a0a0b0;--module-core:#6580f5;--module-module:#60a5fa;--module-unavailable:#ef4444;--readiness-ready:#3cb371;--readiness-warning:#f0b840;--readiness-blocked:#ef4444}body{font-family:var(--font-sans);background:var(--background);color:var(--foreground);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0}code,.font-mono{font-family:var(--font-mono)}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}