@kaddo/cli 3.4.0 → 3.5.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.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/dist/index.js +187 -57
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -436,6 +436,8 @@ create --from roadmap → owners → guard → explain`.
436
436
  | v3.2 | New-project flow hardening: agents in per-layer folders; `new` recommends capability+architecture agents; explain Work Item parser fix; intent vs reality (codebase vs current-state) |
437
437
  | v3.3 | Work Item delivery lifecycle: `understand` shows branch → scan → ownership → guard → knowledge → commit for active Work Items (suggestions only; Kaddo never runs git) |
438
438
  | v3.4 | Delivery protocol in the `work-item-agent`: branch first per the Git strategy, commit only with human confirmation (CLI never touches git) |
439
+ | v3.5 | Knowledge discovery by front-matter type with per-layer maturity (explain/understand/context); context pack carries Operating Rules so agents never commit without confirmation |
440
+ | v3.5.1 | Author attribution & knowledge identity: Kaddo as a practical implementation of KDD for AI-assisted development (docs/README) |
439
441
 
440
442
  **Optional modules (installed with `kaddo add`):**
441
443
 
package/dist/index.js CHANGED
@@ -2592,6 +2592,14 @@ var agentReadme = {
2592
2592
  "",
2593
2593
  "**Kaddo does not execute these agents.** The CLI prepares context; the LLM interprets.",
2594
2594
  "",
2595
+ "## Operating rules (apply to every agent)",
2596
+ "",
2597
+ "- **Never run `git commit`, `git push` or `git merge` without explicit human confirmation.**",
2598
+ "- Never push or merge automatically. Suggest a Conventional Commit message and wait.",
2599
+ "- When implementing a Work Item, create a branch first (per the Git strategy in",
2600
+ " `.kaddo/git.yml`); never work directly on `main`.",
2601
+ "- The Kaddo CLI never calls an LLM and never runs git \u2014 every git action is the human\u2019s.",
2602
+ "",
2595
2603
  "## How to use",
2596
2604
  "",
2597
2605
  "1. Run `kaddo scan` then `kaddo context` to generate `.kaddo/context-pack.md`.",
@@ -4263,59 +4271,158 @@ function runIgnoreRemove(artifactId) {
4263
4271
  import matter2 from "gray-matter";
4264
4272
  import { parse as parseYaml8 } from "yaml";
4265
4273
 
4266
- // src/core/layers.ts
4274
+ // src/core/knowledge-discovery.ts
4267
4275
  var KNOWLEDGE = "knowledge";
4268
- var LAYER_SPEC = [
4269
- {
4270
- layer: "Business",
4271
- items: [
4272
- { name: "problem", path: `${KNOWLEDGE}/business/problem.md` },
4273
- { name: "users", path: `${KNOWLEDGE}/business/users.md` },
4274
- { name: "value-proposition", path: `${KNOWLEDGE}/business/value-proposition.md` },
4275
- { name: "constraints", path: `${KNOWLEDGE}/business/constraints.md` },
4276
- { name: "business-rules", path: `${KNOWLEDGE}/business/business-rules.md` }
4277
- ]
4278
- },
4279
- {
4280
- layer: "Product",
4281
- items: [
4282
- { name: "product brief", path: `${KNOWLEDGE}/product/product-brief.md` },
4283
- { name: "capabilities", path: `${KNOWLEDGE}/product/capabilities.md` }
4284
- ]
4285
- },
4286
- {
4287
- layer: "Tech",
4288
- items: [
4289
- { name: "codebase", path: `${KNOWLEDGE}/tech/codebase.md` },
4290
- { name: "current-state", path: `${KNOWLEDGE}/tech/current-state.md` },
4291
- { name: "decisions", path: `${KNOWLEDGE}/tech/decisions` }
4292
- ]
4293
- },
4294
- {
4295
- layer: "Delivery",
4296
- items: [
4297
- { name: "roadmap", path: `${KNOWLEDGE}/delivery/roadmap.md` },
4298
- { name: "work items", path: `${KNOWLEDGE}/delivery/work-items` }
4299
- ]
4276
+ var CONSOLIDATED_TYPE = {
4277
+ Business: "business",
4278
+ Product: "product",
4279
+ Tech: "codebase"
4280
+ };
4281
+ var STRUCTURED_TYPES = {
4282
+ Business: /* @__PURE__ */ new Set(["problem", "users", "value-proposition", "business-rules", "constraints", "glossary"]),
4283
+ Product: /* @__PURE__ */ new Set(["product-brief", "capabilities"]),
4284
+ Tech: /* @__PURE__ */ new Set([
4285
+ "current-state",
4286
+ "architecture-notes",
4287
+ "decision-candidates",
4288
+ "quality-attributes",
4289
+ "stack",
4290
+ "standards",
4291
+ "security",
4292
+ "git-strategy",
4293
+ "module-design",
4294
+ "adr",
4295
+ "decision"
4296
+ ]),
4297
+ Delivery: /* @__PURE__ */ new Set([])
4298
+ };
4299
+ var WORK_ITEM_TYPES = /* @__PURE__ */ new Set([
4300
+ "work-item",
4301
+ "feature",
4302
+ "bugfix",
4303
+ "hotfix",
4304
+ "spike"
4305
+ ]);
4306
+ function layerForType(type) {
4307
+ if (!type) return null;
4308
+ if (type === CONSOLIDATED_TYPE.Business || STRUCTURED_TYPES.Business.has(type)) return "Business";
4309
+ if (type === CONSOLIDATED_TYPE.Product || STRUCTURED_TYPES.Product.has(type)) return "Product";
4310
+ if (type === CONSOLIDATED_TYPE.Tech || STRUCTURED_TYPES.Tech.has(type) || type === "knowledge")
4311
+ return "Tech";
4312
+ if (type === "roadmap" || WORK_ITEM_TYPES.has(type)) return "Delivery";
4313
+ return null;
4314
+ }
4315
+ function layerFromPath(filePath) {
4316
+ const p2 = filePath.replace(/\\/g, "/");
4317
+ if (p2.includes(`/${KNOWLEDGE}/business/`)) return "Business";
4318
+ if (p2.includes(`/${KNOWLEDGE}/product/`)) return "Product";
4319
+ if (p2.includes(`/${KNOWLEDGE}/tech/`)) return "Tech";
4320
+ if (p2.includes(`/${KNOWLEDGE}/delivery/`)) return "Delivery";
4321
+ return null;
4322
+ }
4323
+ function basename(p2) {
4324
+ return p2.replace(/\\/g, "/").split("/").pop() ?? p2;
4325
+ }
4326
+ function discoverLayers(dir) {
4327
+ const acc = {
4328
+ Business: blank(),
4329
+ Product: blank(),
4330
+ Tech: blank(),
4331
+ Delivery: blank()
4332
+ };
4333
+ const archDir = join(dir, KNOWLEDGE);
4334
+ const artifacts = exists(archDir) ? readArtifacts(archDir) : [];
4335
+ for (const a of artifacts) {
4336
+ const type = a.type;
4337
+ const layer = layerForType(type) ?? layerFromPath(a.filePath);
4338
+ if (!layer) continue;
4339
+ const slot = acc[layer];
4340
+ slot.detected.add(basename(a.filePath));
4341
+ if (layer === "Delivery") {
4342
+ if (type === "roadmap" || basename(a.filePath) === "roadmap.md") slot.hasRoadmap = true;
4343
+ if (WORK_ITEM_TYPES.has(type) || a.filePath.replace(/\\/g, "/").includes("/delivery/work-items/")) {
4344
+ if (a.filePath.replace(/\\/g, "/").includes("/delivery/work-items/")) slot.hasWorkItem = true;
4345
+ }
4346
+ continue;
4347
+ }
4348
+ if (type === CONSOLIDATED_TYPE[layer]) slot.consolidated = true;
4349
+ else if (STRUCTURED_TYPES[layer].has(type)) slot.structured = true;
4350
+ else if (!type) {
4351
+ slot.consolidated = true;
4352
+ }
4353
+ if (type === "adr" || type === "decision") slot.hasDecision = true;
4300
4354
  }
4301
- ];
4302
- function knowledgeLayers(dir) {
4303
- return LAYER_SPEC.map(({ layer, items }) => ({
4355
+ if (existsDirWithMd(join(dir, KNOWLEDGE, "tech", "decisions"))) acc.Tech.structured = true;
4356
+ return ["Business", "Product", "Tech", "Delivery"].map((layer) => ({
4304
4357
  layer,
4305
- items: items.map((it) => ({ name: it.name, present: exists(join(dir, it.path)) }))
4358
+ status: statusFor(layer, acc[layer]),
4359
+ detected: [...acc[layer].detected].sort()
4306
4360
  }));
4307
4361
  }
4362
+ function blank() {
4363
+ return {
4364
+ consolidated: false,
4365
+ structured: false,
4366
+ hasRoadmap: false,
4367
+ hasWorkItem: false,
4368
+ hasDecision: false,
4369
+ detected: /* @__PURE__ */ new Set()
4370
+ };
4371
+ }
4372
+ function existsDirWithMd(d) {
4373
+ if (!exists(d)) return false;
4374
+ try {
4375
+ return readDir(d).some((e) => e.endsWith(".md") && isFile(join(d, e)));
4376
+ } catch {
4377
+ return false;
4378
+ }
4379
+ }
4380
+ function statusFor(layer, a) {
4381
+ if (layer === "Delivery") {
4382
+ if (a.hasWorkItem) return "Traceable";
4383
+ if (a.hasRoadmap) return "Partial";
4384
+ return "Missing";
4385
+ }
4386
+ if (a.structured) return "Structured";
4387
+ if (a.consolidated) return "Consolidated";
4388
+ return "Missing";
4389
+ }
4390
+ function roadmapHasUnmaterializedCandidates(dir) {
4391
+ const roadmap = join(dir, KNOWLEDGE, "delivery", "roadmap.md");
4392
+ if (!exists(roadmap)) return false;
4393
+ let hasCandidates = false;
4394
+ try {
4395
+ hasCandidates = /WI-[A-Z0-9-]*\d/i.test(readFile(roadmap));
4396
+ } catch {
4397
+ return false;
4398
+ }
4399
+ if (!hasCandidates) return false;
4400
+ const wiDir = join(dir, KNOWLEDGE, "delivery", "work-items");
4401
+ return !existsDirWithMd(wiDir);
4402
+ }
4403
+
4404
+ // src/core/layers.ts
4405
+ function knowledgeLayers(dir) {
4406
+ return discoverLayers(dir);
4407
+ }
4408
+ var COMPLETE = {
4409
+ Business: (s) => s !== "Missing",
4410
+ Product: (s) => s !== "Missing",
4411
+ Tech: (s) => s !== "Missing",
4412
+ Delivery: (s) => s === "Traceable"
4413
+ };
4308
4414
  function currentPhase(layers) {
4309
- for (const { layer, items } of layers) {
4310
- if (items.some((i) => !i.present)) return layer;
4415
+ for (const l of layers) {
4416
+ if (!COMPLETE[l.layer](l.status)) return l.layer;
4311
4417
  }
4312
4418
  return "Delivery";
4313
4419
  }
4314
4420
  function renderLayersMarkdown(layers) {
4315
4421
  const lines = [];
4316
- for (const { layer, items } of layers) {
4317
- lines.push(`### ${layer}`);
4318
- for (const it of items) lines.push(`- ${it.present ? "\u2713" : "\u2717"} ${it.name}`);
4422
+ for (const { layer, status, detected } of layers) {
4423
+ lines.push(`### ${layer} \u2014 ${status}`);
4424
+ if (detected.length > 0) for (const d of detected) lines.push(`- \u2713 ${d}`);
4425
+ else lines.push("- \u2717 none");
4319
4426
  lines.push("");
4320
4427
  }
4321
4428
  return lines.join("\n").trimEnd();
@@ -4386,14 +4493,16 @@ function buildProjectExplanation(dir) {
4386
4493
  contractFiles: scan2.contractFiles,
4387
4494
  infrastructureFiles: scan2.infrastructureFiles
4388
4495
  } : null;
4496
+ const layers = knowledgeLayers(dir);
4497
+ const layerStatus = (name) => layers.find((l) => l.layer === name)?.status ?? "Missing";
4389
4498
  const knowledge = {
4390
4499
  hasScan: scan2 !== null,
4391
4500
  hasInventory: exists(join(dir, ARCH_DIR3, "inventory.md")),
4392
4501
  hasContextPack: exists(join(dir, ".kaddo", "context-pack.md")),
4393
4502
  hasUnderstand: exists(join(dir, ".kaddo", "understand.md")),
4394
- hasCapabilities: exists(join(dir, ARCH_DIR3, "product", "capabilities.md")),
4395
- hasArchitecture: exists(join(dir, ARCH_DIR3, "tech", "current-state.md")),
4396
- hasRoadmap: exists(join(dir, ARCH_DIR3, "delivery", "roadmap.md")),
4503
+ hasCapabilities: layerStatus("Product") !== "Missing",
4504
+ hasArchitecture: layerStatus("Tech") !== "Missing",
4505
+ hasRoadmap: layerStatus("Delivery") !== "Missing",
4397
4506
  hasAgents: hasAgents(dir)
4398
4507
  };
4399
4508
  const archDir = join(dir, ARCH_DIR3);
@@ -4429,10 +4538,8 @@ function buildProjectExplanation(dir) {
4429
4538
  if (!knowledge.hasScan) missingKnowledge.push("Scan baseline (.kaddo/scan.json)");
4430
4539
  if (!knowledge.hasContextPack) missingKnowledge.push("Context pack (.kaddo/context-pack.md)");
4431
4540
  if (!knowledge.hasInventory) missingKnowledge.push("Inventory (knowledge/inventory.md)");
4432
- if (!knowledge.hasCapabilities)
4433
- missingKnowledge.push("Capabilities (knowledge/product/capabilities.md)");
4434
- if (!knowledge.hasArchitecture)
4435
- missingKnowledge.push("Architecture baseline (knowledge/tech/current-state.md)");
4541
+ if (!knowledge.hasCapabilities) missingKnowledge.push("Product knowledge (knowledge/product/)");
4542
+ if (!knowledge.hasArchitecture) missingKnowledge.push("Tech knowledge (knowledge/tech/)");
4436
4543
  if (!knowledge.hasRoadmap) missingKnowledge.push("Roadmap (knowledge/delivery/roadmap.md)");
4437
4544
  if (!knowledge.hasAgents) missingKnowledge.push("Agents (knowledge/agents/)");
4438
4545
  if (items.length === 0) missingKnowledge.push("Work items (knowledge/delivery/work-items/)");
@@ -4468,7 +4575,7 @@ function buildProjectExplanation(dir) {
4468
4575
  workItems,
4469
4576
  ownership,
4470
4577
  domains,
4471
- layers: knowledgeLayers(dir),
4578
+ layers,
4472
4579
  mappedModules,
4473
4580
  missingKnowledge,
4474
4581
  suggestedNextSteps
@@ -4503,14 +4610,14 @@ function renderExplanationHuman(exp) {
4503
4610
  lines.push(`- Infrastructure: ${exp.stack.infrastructureFiles.join(", ")}`);
4504
4611
  lines.push("");
4505
4612
  }
4613
+ const ls = (name) => exp.layers.find((l) => l.layer === name)?.status ?? "Missing";
4506
4614
  lines.push("## Knowledge Status");
4507
4615
  lines.push(`- Inventory: ${exp.knowledge.hasInventory ? "available" : "missing"}`);
4508
4616
  lines.push(`- Context pack: ${exp.knowledge.hasContextPack ? "available" : "missing"}`);
4509
- lines.push(`- Capabilities: ${exp.knowledge.hasCapabilities ? "available" : "missing"}`);
4510
- lines.push(
4511
- `- Architecture baseline: ${exp.knowledge.hasArchitecture ? "available" : "missing"}`
4512
- );
4513
- lines.push(`- Roadmap: ${exp.knowledge.hasRoadmap ? "available" : "missing"}`);
4617
+ lines.push(`- Business: ${ls("Business")}`);
4618
+ lines.push(`- Product: ${ls("Product")}`);
4619
+ lines.push(`- Tech: ${ls("Tech")}`);
4620
+ lines.push(`- Delivery: ${ls("Delivery")}`);
4514
4621
  lines.push(`- Agents: ${exp.knowledge.hasAgents ? "available" : "missing"}`);
4515
4622
  lines.push(`- Work items: ${exp.workItems.total}`);
4516
4623
  lines.push(
@@ -4818,6 +4925,13 @@ var LLM_INSTRUCTIONS = [
4818
4925
  "First extract: system capabilities, architecture notes, risks, open questions and roadmap candidates.",
4819
4926
  "This pack is deterministic CLI output \u2014 it does not interpret the system. That is your job."
4820
4927
  ];
4928
+ var OPERATING_RULES = [
4929
+ "**Never run `git commit`, `git push` or `git merge` without explicit human confirmation.**",
4930
+ "Never push or merge automatically \u2014 ever. Suggest a Conventional Commit message and wait.",
4931
+ "When implementing a Work Item, create a branch FIRST (per the project Git strategy, `.kaddo/git.yml`, default `feature/<id>-<slug>`). Never work directly on `main`.",
4932
+ "After significant changes run `kaddo scan`, `kaddo owners suggest` and `kaddo guard`, and update the affected knowledge (ADR / capabilities / current-state).",
4933
+ "Kaddo itself never calls an LLM and never runs git \u2014 every git action is the human\u2019s."
4934
+ ];
4821
4935
  function toContextWorkItem(a) {
4822
4936
  return {
4823
4937
  id: a.id,
@@ -4893,7 +5007,8 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
4893
5007
  handoff: {
4894
5008
  recommendedAgents: recommendedAgentsForState(state),
4895
5009
  nextSteps: nextStepsForState2(state),
4896
- instructions: LLM_INSTRUCTIONS
5010
+ instructions: LLM_INSTRUCTIONS,
5011
+ operatingRules: OPERATING_RULES
4897
5012
  }
4898
5013
  };
4899
5014
  }
@@ -4912,6 +5027,8 @@ function renderContextPack(pack) {
4912
5027
  parts.push(
4913
5028
  "> Generated by `kaddo context`. Deterministic CLI output \u2014 not an interpretation of the system.\n"
4914
5029
  );
5030
+ parts.push("## Operating Rules (read first)\n");
5031
+ parts.push(handoff.operatingRules.map((r) => `- ${r}`).join("\n") + "\n");
4915
5032
  parts.push("## Project Metadata\n");
4916
5033
  parts.push(
4917
5034
  [
@@ -4925,6 +5042,9 @@ function renderContextPack(pack) {
4925
5042
  parts.push(
4926
5043
  "Project knowledge is organized in four layers: **Business \u2192 Product \u2192 Tech \u2192 Delivery**.\n"
4927
5044
  );
5045
+ const maturity = pack.layers.map((l) => `${l.layer}: ${l.status}`).join(" \xB7 ");
5046
+ parts.push(`Knowledge maturity \u2014 ${maturity}
5047
+ `);
4928
5048
  parts.push(renderLayersMarkdown(pack.layers) + "\n");
4929
5049
  parts.push("## Technical Inventory\n");
4930
5050
  if (scan2.available) {
@@ -5318,6 +5438,12 @@ function runUnderstand() {
5318
5438
  ],
5319
5439
  utilities: ["Use legacy-agent to surface risks and unknowns"]
5320
5440
  };
5441
+ if (roadmapHasUnmaterializedCandidates(dir)) {
5442
+ console.log("");
5443
+ console.log("The roadmap has Work Item candidates that are not materialized yet.");
5444
+ console.log(" \u2192 Run `kaddo create --from roadmap`, or use the work-item-agent to");
5445
+ console.log(" materialize them into knowledge/delivery/work-items/.");
5446
+ }
5321
5447
  console.log("");
5322
5448
  console.log(`Current phase: ${phase}`);
5323
5449
  console.log("Recommended next steps:");
@@ -8008,6 +8134,10 @@ async function runBootstrap(dir = cwd()) {
8008
8134
  for (const p2 of result.skipped) console.log(` - ${p2}`);
8009
8135
  }
8010
8136
  console.log("");
8137
+ console.log("");
8138
+ log2.info(
8139
+ "When you pass the context pack to your LLM/coding agent, it must never commit, push or merge without your confirmation \u2014 and create a branch before implementing."
8140
+ );
8011
8141
  outro2(
8012
8142
  "Minimal knowledge base ready (Business \u2192 Product \u2192 Tech). Run `kaddo context` and `kaddo add agents`, then refine with the business-agent, bootstrap-agent and codebase-agent. Roadmap and work items come next under knowledge/delivery/."
8013
8143
  );
@@ -8015,7 +8145,7 @@ async function runBootstrap(dir = cwd()) {
8015
8145
 
8016
8146
  // src/index.ts
8017
8147
  var program = new Command();
8018
- program.name("kaddo").description("Knowledge Driven Development toolkit").version("3.4.0");
8148
+ program.name("kaddo").description("Knowledge Driven Development toolkit").version("3.5.1");
8019
8149
  program.command("init").description("Initialize Kaddo in the current project").action(async () => {
8020
8150
  await runInit();
8021
8151
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/cli",
3
- "version": "3.4.0",
3
+ "version": "3.5.1",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {