@kaddo/cli 3.15.0 → 3.16.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/README.md +41 -4
- package/dist/index.js +468 -57
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,6 +71,10 @@ knowledge/
|
|
|
71
71
|
config.yml ← project config
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
Asks for the project state (`new | pre-ai | legacy`), team size, repo structure and the
|
|
75
|
+
**project knowledge language** (`en | es`) — the language your knowledge artifacts are
|
|
76
|
+
written in; the CLI itself stays English.
|
|
77
|
+
|
|
74
78
|
---
|
|
75
79
|
|
|
76
80
|
### `kaddo scan`
|
|
@@ -265,12 +269,17 @@ concrete step (`kaddo scan` or `kaddo add agents`).
|
|
|
265
269
|
Create a Work Item with the minimum context for its Knowledge Level.
|
|
266
270
|
|
|
267
271
|
```bash
|
|
268
|
-
kaddo create feature # K2:
|
|
269
|
-
kaddo create bugfix # K2:
|
|
270
|
-
kaddo create hotfix # K1:
|
|
271
|
-
kaddo create spike # K3:
|
|
272
|
+
kaddo create feature # K2: delivers a user-facing capability
|
|
273
|
+
kaddo create bugfix # K2: fixes a known defect
|
|
274
|
+
kaddo create hotfix # K1: urgent fix on a released version
|
|
275
|
+
kaddo create spike # K3: exploratory / reduce uncertainty
|
|
276
|
+
kaddo create chore # K1: maintenance, tooling, config, infra
|
|
272
277
|
```
|
|
273
278
|
|
|
279
|
+
New Work Items land in `knowledge/delivery/work-items/draft/` with `status: draft`
|
|
280
|
+
(lifecycle: `draft → ready → in-progress → blocked → completed → archived`). Aliases like
|
|
281
|
+
`setup`, `tooling`, `maintenance`, `infrastructure` or `refactor` resolve to `chore`.
|
|
282
|
+
|
|
274
283
|
**From a roadmap candidate:**
|
|
275
284
|
|
|
276
285
|
```bash
|
|
@@ -409,6 +418,25 @@ architecture baseline, roadmap, agents), work items, **ownership coverage**, the
|
|
|
409
418
|
`kaddo explain` summarizes what Kaddo already knows. The focused flags
|
|
410
419
|
(`--scope`, `--type`, `--since`) still explain a subset of artifacts.
|
|
411
420
|
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
### `kaddo capsule`
|
|
424
|
+
|
|
425
|
+
Share or consume minimal, portable knowledge about a system — a **Knowledge Capsule** —
|
|
426
|
+
without mapping it as multirepo or reading its source.
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
kaddo capsule export # → .kaddo/exports/<project>.capsule.md / .json
|
|
430
|
+
kaddo capsule add <path> # import an external capsule → external/<id>.capsule.md
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
`export` builds a deterministic draft from `knowledge/` (purpose, capabilities, public
|
|
434
|
+
contracts, risks, ADRs, owners — never source code or secrets); refine it with the
|
|
435
|
+
`capsule-agent` before sharing. `add` registers the capsule in `.kaddo/external.yml`;
|
|
436
|
+
`kaddo context` then includes an **External Knowledge** section and `kaddo explain` lists
|
|
437
|
+
the capsules (warning when one looks stale). See the
|
|
438
|
+
[Knowledge Capsules guide](https://kaddo.trycatch.tv/knowledge-capsules/).
|
|
439
|
+
|
|
412
440
|
## Roadmap
|
|
413
441
|
|
|
414
442
|
The full knowledge loop ships today: `scan → context → agents → understand → roadmap →
|
|
@@ -441,6 +469,15 @@ create --from roadmap → owners → guard → explain`.
|
|
|
441
469
|
| v3.6 | Flexible roadmap parsing and roadmap candidate/materialized Work Item reporting |
|
|
442
470
|
| v3.7 | Work Item lifecycle active workspace (`draft`, `ready`, `in-progress`, `blocked`, `completed`, `archived`) |
|
|
443
471
|
| v3.7.1 | Context Efficiency positioning: Repository Exploration Tax and structured-knowledge narrative |
|
|
472
|
+
| v3.8 | Agent Trace & responsibility boundaries: every prompt declares Agent/Produced/Next; new `implementation-agent` (the only agent that may suggest a branch) |
|
|
473
|
+
| v3.9 | New `chore` Work Item type (+ aliases setup/tooling/maintenance/infra); explain Work Items by Type; context Delivery Mix |
|
|
474
|
+
| v3.9.1 | Unified knowledge artifact discovery: one service behind explain/context/owners/guard (fixes owners missing lifecycle subfolders) |
|
|
475
|
+
| v3.10 | State-aware recommendations: real phase model (Discovery → Planning → Delivery Preparation → Active Delivery → Maintenance) drives understand/context/explain |
|
|
476
|
+
| v3.11 | Command workflow clarity: official command matrix + "Question answered / Suggested next" footer on scan/context/explain/understand; `chore/` branch prefix |
|
|
477
|
+
| v3.13 | New `backlog-agent`: capture raw ideas into a Work Item draft or roadmap candidate (human decides the next step) |
|
|
478
|
+
| v3.14 | Project knowledge language (`project.language: en\|es`): knowledge in your language, CLI stays English; all agents respect it |
|
|
479
|
+
| v3.15 | Delivery context consistency: phase-based handoff + per-phase LLM instructions; assisted `owners suggest` (normalize/validate globs); new `ownership-agent`; guard untracked-files warning; duplicate Work Item detection |
|
|
480
|
+
| v3.16 | Knowledge Capsules: `kaddo capsule export/add`, External Knowledge in context/explain, new `capsule-agent` |
|
|
444
481
|
|
|
445
482
|
**Optional modules (installed with `kaddo add`):**
|
|
446
483
|
|
package/dist/index.js
CHANGED
|
@@ -600,6 +600,21 @@ ${issues}`);
|
|
|
600
600
|
}
|
|
601
601
|
return parsed.data;
|
|
602
602
|
}
|
|
603
|
+
function requireConfig(dir) {
|
|
604
|
+
let config;
|
|
605
|
+
try {
|
|
606
|
+
config = loadConfig(dir);
|
|
607
|
+
} catch (err) {
|
|
608
|
+
const message = err instanceof ConfigError ? err.message : String(err);
|
|
609
|
+
console.error(message);
|
|
610
|
+
process.exit(1);
|
|
611
|
+
}
|
|
612
|
+
if (!config) {
|
|
613
|
+
console.error("No .kaddo/config.yml found. Run `kaddo init` first.");
|
|
614
|
+
process.exit(1);
|
|
615
|
+
}
|
|
616
|
+
return config;
|
|
617
|
+
}
|
|
603
618
|
function nextStepsForState(state) {
|
|
604
619
|
switch (state) {
|
|
605
620
|
case "new":
|
|
@@ -657,7 +672,15 @@ var COMMAND_HELP = {
|
|
|
657
672
|
question: "Is knowledge drifting from code?",
|
|
658
673
|
next: "Update the affected knowledge, then commit"
|
|
659
674
|
},
|
|
660
|
-
"add agents": { question: "Which agents are available?", next: "kaddo understand" }
|
|
675
|
+
"add agents": { question: "Which agents are available?", next: "kaddo understand" },
|
|
676
|
+
"capsule export": {
|
|
677
|
+
question: "How do I share this project as external context?",
|
|
678
|
+
next: "Refine with the capsule-agent, then share the capsule file"
|
|
679
|
+
},
|
|
680
|
+
"capsule add": {
|
|
681
|
+
question: "How do I consume another system as external context?",
|
|
682
|
+
next: "kaddo context (the pack now includes External Knowledge)"
|
|
683
|
+
}
|
|
661
684
|
};
|
|
662
685
|
function commandFooterLines(name) {
|
|
663
686
|
const help = COMMAND_HELP[name];
|
|
@@ -1611,6 +1634,20 @@ var RESPONSIBILITY_MATRIX = {
|
|
|
1611
1634
|
],
|
|
1612
1635
|
next: ["kaddo scan", "kaddo owners suggest", "kaddo guard", "kaddo explain"]
|
|
1613
1636
|
},
|
|
1637
|
+
"capsule-agent": {
|
|
1638
|
+
agent: "capsule-agent",
|
|
1639
|
+
responsibleFor: ["Refining/validating a Knowledge Capsule for external sharing"],
|
|
1640
|
+
produces: [".kaddo/exports/<system>.capsule.md"],
|
|
1641
|
+
canSuggest: ["kaddo capsule export"],
|
|
1642
|
+
cannotSuggest: [
|
|
1643
|
+
"exporting secrets",
|
|
1644
|
+
"exporting source code",
|
|
1645
|
+
"inventing contracts",
|
|
1646
|
+
"code",
|
|
1647
|
+
"git"
|
|
1648
|
+
],
|
|
1649
|
+
next: ["kaddo capsule export"]
|
|
1650
|
+
},
|
|
1614
1651
|
"ownership-agent": {
|
|
1615
1652
|
agent: "ownership-agent",
|
|
1616
1653
|
responsibleFor: ["Precise code: ownership for Work Items and artifacts"],
|
|
@@ -2932,6 +2969,85 @@ Code, tests and migrations live in the repository. Knowledge updates go under \`
|
|
|
2932
2969
|
- Affected knowledge is updated.
|
|
2933
2970
|
- Commit is suggested and awaits human confirmation \u2014 never run automatically.
|
|
2934
2971
|
`;
|
|
2972
|
+
var CAPSULE_AGENT = `# Capsule Agent
|
|
2973
|
+
|
|
2974
|
+
## Role
|
|
2975
|
+
|
|
2976
|
+
You are the Kaddo Capsule Agent. Your job is to refine and validate a **Knowledge Capsule** \u2014 a
|
|
2977
|
+
minimal, portable summary another project can consume as external context \u2014 before it is exported.
|
|
2978
|
+
|
|
2979
|
+
You do not write code, you never invent contracts, and you mark uncertainties. A capsule contains
|
|
2980
|
+
**knowledge, not code or secrets**.
|
|
2981
|
+
|
|
2982
|
+
## When to Use
|
|
2983
|
+
|
|
2984
|
+
Use this agent before sharing a Knowledge Capsule (after \`kaddo capsule export\` produced a draft),
|
|
2985
|
+
to sharpen its purpose, capabilities, public contracts, risks, owners and out-of-scope.
|
|
2986
|
+
|
|
2987
|
+
## Input Required
|
|
2988
|
+
|
|
2989
|
+
Provide \`.kaddo/context-pack.md\` plus \`knowledge/product/capabilities.md\`,
|
|
2990
|
+
\`knowledge/tech/current-state.md\`, \`knowledge/tech/decisions/\` and any contracts
|
|
2991
|
+
(\`knowledge/tech/contracts/\`) that exist. Also provide the draft capsule from
|
|
2992
|
+
\`.kaddo/exports/<system>.capsule.md\`.
|
|
2993
|
+
|
|
2994
|
+
## Expected Output
|
|
2995
|
+
|
|
2996
|
+
A refined Markdown capsule intended to be saved as \`.kaddo/exports/<system>.capsule.md\`.
|
|
2997
|
+
|
|
2998
|
+
## Instructions
|
|
2999
|
+
|
|
3000
|
+
1. Summarize what the system does and the boundaries of this capsule.
|
|
3001
|
+
2. List the **public contracts** consumers integrate with (APIs, events) \u2014 never invent them.
|
|
3002
|
+
3. List exposed capabilities, dependencies and known integration risks.
|
|
3003
|
+
4. Identify owners and relevant ADRs.
|
|
3004
|
+
5. State what is **out of scope** for this capsule.
|
|
3005
|
+
6. Mark any unknowns explicitly.
|
|
3006
|
+
|
|
3007
|
+
## Constraints
|
|
3008
|
+
|
|
3009
|
+
- Do **not** export secrets, tokens, credentials, private keys, PII or internal sensitive URLs.
|
|
3010
|
+
- Do **not** export source code.
|
|
3011
|
+
- Do **not** invent contracts or integrations.
|
|
3012
|
+
- Summarize and mark boundaries; prefer "unknown" over guessing.
|
|
3013
|
+
|
|
3014
|
+
## Output Format
|
|
3015
|
+
|
|
3016
|
+
\`\`\`markdown
|
|
3017
|
+
---
|
|
3018
|
+
type: knowledge-capsule
|
|
3019
|
+
system: <system>
|
|
3020
|
+
version: 1
|
|
3021
|
+
updated_at: <YYYY-MM-DD>
|
|
3022
|
+
owner: <team>
|
|
3023
|
+
---
|
|
3024
|
+
|
|
3025
|
+
# <System> \u2014 Knowledge Capsule
|
|
3026
|
+
|
|
3027
|
+
## Purpose
|
|
3028
|
+
## Responsibilities
|
|
3029
|
+
## Exposed Capabilities
|
|
3030
|
+
## Public Contracts
|
|
3031
|
+
## Dependencies
|
|
3032
|
+
## Known Risks
|
|
3033
|
+
## Relevant ADRs
|
|
3034
|
+
## Owners
|
|
3035
|
+
## Out of Scope
|
|
3036
|
+
## Usage Notes
|
|
3037
|
+
\`\`\`
|
|
3038
|
+
|
|
3039
|
+
## Where to Save the Result
|
|
3040
|
+
|
|
3041
|
+
Save as \`.kaddo/exports/<system>.capsule.md\`. The human reviews the security checklist (no
|
|
3042
|
+
secrets, no source) before sharing.
|
|
3043
|
+
|
|
3044
|
+
## Quality Checklist
|
|
3045
|
+
|
|
3046
|
+
- Purpose and boundaries are clear.
|
|
3047
|
+
- Public contracts are real (not invented) \u2014 unknowns are marked.
|
|
3048
|
+
- Capabilities, dependencies, risks, owners and out-of-scope are present.
|
|
3049
|
+
- No secrets, credentials, PII or source code are included.
|
|
3050
|
+
`;
|
|
2935
3051
|
var OWNERSHIP_AGENT = `# Ownership Agent
|
|
2936
3052
|
|
|
2937
3053
|
## Role
|
|
@@ -3108,7 +3224,9 @@ var AGENT_PROMPTS = [
|
|
|
3108
3224
|
// Backlog capture (idea → draft / roadmap candidate — VS-050)
|
|
3109
3225
|
{ fileName: "backlog-agent.md", content: BACKLOG_AGENT },
|
|
3110
3226
|
// Ownership proposals (precise code: globs — VS-052)
|
|
3111
|
-
{ fileName: "ownership-agent.md", content: OWNERSHIP_AGENT }
|
|
3227
|
+
{ fileName: "ownership-agent.md", content: OWNERSHIP_AGENT },
|
|
3228
|
+
// Knowledge Capsule refinement (external context — VS-054)
|
|
3229
|
+
{ fileName: "capsule-agent.md", content: CAPSULE_AGENT }
|
|
3112
3230
|
// Every official prompt ends with its responsibility boundaries + Agent Trace footer.
|
|
3113
3231
|
].map((p2) => ({ fileName: p2.fileName, content: withResponsibilityTrace(p2.fileName, p2.content) }));
|
|
3114
3232
|
|
|
@@ -3123,7 +3241,8 @@ var AGENT_GROUPS = {
|
|
|
3123
3241
|
"security-agent.md",
|
|
3124
3242
|
"standards-agent.md",
|
|
3125
3243
|
"module-design-agent.md",
|
|
3126
|
-
"adr-agent.md"
|
|
3244
|
+
"adr-agent.md",
|
|
3245
|
+
"capsule-agent.md"
|
|
3127
3246
|
],
|
|
3128
3247
|
delivery: [
|
|
3129
3248
|
"backlog-agent.md",
|
|
@@ -3234,6 +3353,9 @@ var agentReadme = {
|
|
|
3234
3353
|
"- **pre-ai** \u2192 capability-agent \u2192 architecture-agent \u2192 roadmap-agent",
|
|
3235
3354
|
"- **legacy** \u2192 legacy-agent \u2192 architecture-agent \u2192 capability-agent \u2192 roadmap-agent",
|
|
3236
3355
|
"",
|
|
3356
|
+
"Then, in delivery: backlog-agent (capture ideas) \u2192 work-item-agent (refine) \u2192",
|
|
3357
|
+
"ownership-agent (propose code: globs) \u2192 implementation-agent (build).",
|
|
3358
|
+
"",
|
|
3237
3359
|
"## Installed agents",
|
|
3238
3360
|
"",
|
|
3239
3361
|
"### Bootstrap agents (new projects)",
|
|
@@ -3250,14 +3372,22 @@ var agentReadme = {
|
|
|
3250
3372
|
"- `legacy-agent.md` \u2014 analyze risks/unknowns before changing legacy code.",
|
|
3251
3373
|
"- `adr-agent.md` \u2014 propose candidate architecture decisions.",
|
|
3252
3374
|
"",
|
|
3253
|
-
"###
|
|
3375
|
+
"### Delivery agents",
|
|
3254
3376
|
"",
|
|
3377
|
+
"- `backlog-agent.md` \u2014 capture raw ideas/notes into a Work Item draft or roadmap candidate.",
|
|
3255
3378
|
"- `work-item-agent.md` \u2014 refine roadmap candidates or existing Work Items.",
|
|
3379
|
+
"- `implementation-agent.md` \u2014 implement a refined Work Item (the only agent that may",
|
|
3380
|
+
" suggest a branch; never runs git).",
|
|
3381
|
+
"- `ownership-agent.md` \u2014 propose precise `code:` globs (human applies with `kaddo owners suggest`).",
|
|
3256
3382
|
"- `git-strategy-agent.md` \u2014 define branch/commit/tag/release strategy.",
|
|
3383
|
+
"",
|
|
3384
|
+
"### Operational agents",
|
|
3385
|
+
"",
|
|
3257
3386
|
"- `security-agent.md` \u2014 document security considerations (no scanning).",
|
|
3258
3387
|
"- `standards-agent.md` \u2014 propose lightweight coding/docs/architecture standards.",
|
|
3259
3388
|
"- `stack-agent.md` \u2014 document technologies and stack decisions.",
|
|
3260
|
-
"- `module-design-agent.md` \u2014 document the design of a mapped module."
|
|
3389
|
+
"- `module-design-agent.md` \u2014 document the design of a mapped module.",
|
|
3390
|
+
"- `capsule-agent.md` \u2014 refine a Knowledge Capsule for external sharing (no secrets/source)."
|
|
3261
3391
|
].join("\n")
|
|
3262
3392
|
};
|
|
3263
3393
|
var agentFiles = AGENT_PROMPTS.map((a) => ({
|
|
@@ -3844,7 +3974,7 @@ function formatList(items) {
|
|
|
3844
3974
|
return items.map((i) => `- ${i.trim()}`).join("\n");
|
|
3845
3975
|
}
|
|
3846
3976
|
function buildFrontMatter(id, type, level, title, answers) {
|
|
3847
|
-
const
|
|
3977
|
+
const today2 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3848
3978
|
const lines = [
|
|
3849
3979
|
"---",
|
|
3850
3980
|
`type: ${type}`,
|
|
@@ -3856,7 +3986,7 @@ function buildFrontMatter(id, type, level, title, answers) {
|
|
|
3856
3986
|
`initiative:`,
|
|
3857
3987
|
`domains: []`,
|
|
3858
3988
|
`code: []`,
|
|
3859
|
-
`created_at: ${
|
|
3989
|
+
`created_at: ${today2}`,
|
|
3860
3990
|
`summary: "${answers.problem?.split(".")[0] ?? title}"`,
|
|
3861
3991
|
"---"
|
|
3862
3992
|
];
|
|
@@ -3926,7 +4056,7 @@ _What did we learn from this change? Update after completion._
|
|
|
3926
4056
|
return sections.join("\n");
|
|
3927
4057
|
}
|
|
3928
4058
|
function buildModuleFrontMatter(id, modType, title, answers) {
|
|
3929
|
-
const
|
|
4059
|
+
const today2 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3930
4060
|
const extra = modType.extraFrontMatter ?? {};
|
|
3931
4061
|
const extraLines = Object.entries(extra).map(
|
|
3932
4062
|
([k, v]) => `${k}: ${JSON.stringify(v)}`
|
|
@@ -3942,7 +4072,7 @@ function buildModuleFrontMatter(id, modType, title, answers) {
|
|
|
3942
4072
|
`initiative:`,
|
|
3943
4073
|
`domains: []`,
|
|
3944
4074
|
`code: []`,
|
|
3945
|
-
`created_at: ${
|
|
4075
|
+
`created_at: ${today2}`,
|
|
3946
4076
|
`summary: "${title}"`,
|
|
3947
4077
|
...extraLines,
|
|
3948
4078
|
"---"
|
|
@@ -4027,8 +4157,8 @@ async function runCreate(type, opts = {}) {
|
|
|
4027
4157
|
answers[question.frontMatterField] = answer.trim();
|
|
4028
4158
|
}
|
|
4029
4159
|
const id = nextWorkItemId(dir);
|
|
4030
|
-
const
|
|
4031
|
-
const fileName = `${id}-${
|
|
4160
|
+
const slug2 = slugify(title);
|
|
4161
|
+
const fileName = `${id}-${slug2}.md`;
|
|
4032
4162
|
const filePath = join(dir, DRAFT_DIR, fileName);
|
|
4033
4163
|
const frontMatter2 = buildFrontMatter(id, workItemType, level, title.trim(), answers);
|
|
4034
4164
|
const body = buildBody(workItemType, level, title.trim(), answers, levelDef.qualityGate);
|
|
@@ -4068,8 +4198,8 @@ async function runCreateModule(dir, modType) {
|
|
|
4068
4198
|
answers[q.frontMatterField] = answer.trim();
|
|
4069
4199
|
}
|
|
4070
4200
|
const id = nextWorkItemId(dir);
|
|
4071
|
-
const
|
|
4072
|
-
const fileName = `${id}-${
|
|
4201
|
+
const slug2 = slugify(title);
|
|
4202
|
+
const fileName = `${id}-${slug2}.md`;
|
|
4073
4203
|
const filePath = join(dir, DRAFT_DIR, fileName);
|
|
4074
4204
|
const frontMatter2 = buildModuleFrontMatter(id, modType, title.trim(), answers);
|
|
4075
4205
|
const body = buildModuleBody(modType, title.trim(), answers);
|
|
@@ -4091,7 +4221,7 @@ function resolveCandidateLevel(candidate, type) {
|
|
|
4091
4221
|
return getLevelForType(type);
|
|
4092
4222
|
}
|
|
4093
4223
|
function buildRoadmapFrontMatter(id, type, level, title, candidate, answers) {
|
|
4094
|
-
const
|
|
4224
|
+
const today2 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4095
4225
|
const summary = (answers.problem?.split(".")[0] ?? candidate.expectedValue ?? title).trim();
|
|
4096
4226
|
const initiative = candidate.initiative?.title ?? candidate.initiative?.id ?? "";
|
|
4097
4227
|
const lines = [
|
|
@@ -4105,7 +4235,7 @@ function buildRoadmapFrontMatter(id, type, level, title, candidate, answers) {
|
|
|
4105
4235
|
`initiative: "${initiative.replace(/"/g, "'")}"`,
|
|
4106
4236
|
`domains: []`,
|
|
4107
4237
|
`code: []`,
|
|
4108
|
-
`created_at: ${
|
|
4238
|
+
`created_at: ${today2}`,
|
|
4109
4239
|
`source: roadmap`,
|
|
4110
4240
|
`source_id: ${candidate.id}`,
|
|
4111
4241
|
`source_initiative: ${candidate.initiative?.id ?? "unknown"}`,
|
|
@@ -4201,8 +4331,8 @@ function buildRoadmapWorkItem(opts) {
|
|
|
4201
4331
|
const { id, type, level, candidate } = opts;
|
|
4202
4332
|
const answers = opts.answers ?? {};
|
|
4203
4333
|
const title = candidate.title.trim();
|
|
4204
|
-
const
|
|
4205
|
-
const fileName = `${id}-${
|
|
4334
|
+
const slug2 = slugify(title);
|
|
4335
|
+
const fileName = `${id}-${slug2}.md`;
|
|
4206
4336
|
const qualityGate = getLevel(level).qualityGate;
|
|
4207
4337
|
const frontMatter2 = buildRoadmapFrontMatter(id, type, level, title, candidate, answers);
|
|
4208
4338
|
const body = buildRoadmapBody(type, level, title, candidate, answers, qualityGate);
|
|
@@ -5125,8 +5255,8 @@ function runIgnoreRemove(artifactId) {
|
|
|
5125
5255
|
}
|
|
5126
5256
|
|
|
5127
5257
|
// src/commands/explain.ts
|
|
5128
|
-
import
|
|
5129
|
-
import { parse as
|
|
5258
|
+
import matter3 from "gray-matter";
|
|
5259
|
+
import { parse as parseYaml9 } from "yaml";
|
|
5130
5260
|
|
|
5131
5261
|
// src/core/delivery-phase.ts
|
|
5132
5262
|
function layer(layers, name) {
|
|
@@ -5262,8 +5392,201 @@ function assessPhase(input) {
|
|
|
5262
5392
|
return { phase, reasons, recommendedAgents: recommendedAgents2, nextStep, llmInstructions };
|
|
5263
5393
|
}
|
|
5264
5394
|
|
|
5265
|
-
// src/core/
|
|
5395
|
+
// src/core/capsule.ts
|
|
5396
|
+
import matter2 from "gray-matter";
|
|
5397
|
+
import { parse as parseYaml8, stringify as stringifyYaml3 } from "yaml";
|
|
5266
5398
|
var KNOWLEDGE = "knowledge";
|
|
5399
|
+
function today() {
|
|
5400
|
+
return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
5401
|
+
}
|
|
5402
|
+
function firstParagraph(md) {
|
|
5403
|
+
const body = md.replace(/^---\n[\s\S]*?\n---\n/, "");
|
|
5404
|
+
for (const block of body.split(/\n\s*\n/)) {
|
|
5405
|
+
const t = block.trim();
|
|
5406
|
+
if (t && !t.startsWith("#") && !t.startsWith(">")) return t.replace(/\s+/g, " ");
|
|
5407
|
+
}
|
|
5408
|
+
return "";
|
|
5409
|
+
}
|
|
5410
|
+
function readIf(dir, rel) {
|
|
5411
|
+
const p2 = join(dir, rel);
|
|
5412
|
+
return exists(p2) ? readFile(p2) : null;
|
|
5413
|
+
}
|
|
5414
|
+
function headings(md) {
|
|
5415
|
+
return md.split(/\r?\n/).map((l) => l.match(/^#{2,3}\s+(.+?)\s*$/)).filter((m) => Boolean(m)).map((m) => m[1].trim()).filter((h) => !/^(summary|resumen|overview|risks|owners|out of scope)$/i.test(h));
|
|
5416
|
+
}
|
|
5417
|
+
function buildCapsule(dir, config) {
|
|
5418
|
+
const ownerMap = loadOwners(dir);
|
|
5419
|
+
const owners = [...new Set(Object.values(ownerMap).flat())];
|
|
5420
|
+
const all = discoverKnowledge(dir);
|
|
5421
|
+
const purposeSrc = readIf(dir, `${KNOWLEDGE}/business/business.md`) ?? readIf(dir, `${KNOWLEDGE}/knowledge.md`) ?? "";
|
|
5422
|
+
const capsMd = readIf(dir, `${KNOWLEDGE}/product/capabilities.md`);
|
|
5423
|
+
const risksMd = readIf(dir, `${KNOWLEDGE}/legacy/risks.md`);
|
|
5424
|
+
const adrs = all.filter((a) => a.filePath.replace(/\\/g, "/").includes("/tech/decisions/") && Boolean(a.type)).map((a) => a.title || a.id).filter(Boolean);
|
|
5425
|
+
return {
|
|
5426
|
+
system: config.project.name,
|
|
5427
|
+
version: 1,
|
|
5428
|
+
updatedAt: today(),
|
|
5429
|
+
owner: owners[0] ?? "unknown",
|
|
5430
|
+
sourceProject: config.project.name,
|
|
5431
|
+
purpose: firstParagraph(purposeSrc),
|
|
5432
|
+
responsibilities: [],
|
|
5433
|
+
capabilities: capsMd ? headings(capsMd) : [],
|
|
5434
|
+
contracts: [],
|
|
5435
|
+
dependencies: [],
|
|
5436
|
+
knownRisks: risksMd ? headings(risksMd) : [],
|
|
5437
|
+
adrs,
|
|
5438
|
+
owners,
|
|
5439
|
+
outOfScope: [],
|
|
5440
|
+
usageNotes: []
|
|
5441
|
+
};
|
|
5442
|
+
}
|
|
5443
|
+
function section2(title, items, placeholder) {
|
|
5444
|
+
if (items.length === 0) return `## ${title}
|
|
5445
|
+
|
|
5446
|
+
_${placeholder}_
|
|
5447
|
+
`;
|
|
5448
|
+
return `## ${title}
|
|
5449
|
+
|
|
5450
|
+
${items.map((i) => `- ${i}`).join("\n")}
|
|
5451
|
+
`;
|
|
5452
|
+
}
|
|
5453
|
+
function renderCapsuleMarkdown(c) {
|
|
5454
|
+
const fm = [
|
|
5455
|
+
"---",
|
|
5456
|
+
"type: knowledge-capsule",
|
|
5457
|
+
`system: ${c.system}`,
|
|
5458
|
+
`version: ${c.version}`,
|
|
5459
|
+
`updated_at: ${c.updatedAt}`,
|
|
5460
|
+
`owner: ${c.owner}`,
|
|
5461
|
+
`source_project: ${c.sourceProject}`,
|
|
5462
|
+
...c.sourceCommit ? [`source_commit: ${c.sourceCommit}`] : [],
|
|
5463
|
+
"---"
|
|
5464
|
+
].join("\n");
|
|
5465
|
+
const parts = [
|
|
5466
|
+
fm,
|
|
5467
|
+
"",
|
|
5468
|
+
`# ${c.system} \u2014 Knowledge Capsule`,
|
|
5469
|
+
"",
|
|
5470
|
+
`## Purpose
|
|
5471
|
+
|
|
5472
|
+
${c.purpose || "_To be completed by the capsule-agent._"}
|
|
5473
|
+
`,
|
|
5474
|
+
section2("Responsibilities", c.responsibilities, "List what this system is responsible for."),
|
|
5475
|
+
section2("Exposed Capabilities", c.capabilities, "List the capabilities this system exposes."),
|
|
5476
|
+
section2("Public Contracts", c.contracts, "List public APIs and events (e.g. `POST /orders`, `OrderCreated`)."),
|
|
5477
|
+
section2("Dependencies", c.dependencies, "List systems this one depends on."),
|
|
5478
|
+
section2("Known Risks", c.knownRisks, "List integration risks consumers should know."),
|
|
5479
|
+
section2("Relevant ADRs", c.adrs, "List decisions that affect how to integrate."),
|
|
5480
|
+
section2("Owners", c.owners, "Who owns this system."),
|
|
5481
|
+
section2("Out of Scope", c.outOfScope, "What this capsule deliberately does not cover."),
|
|
5482
|
+
section2("Usage Notes", c.usageNotes, "How consumers should integrate."),
|
|
5483
|
+
"> Security: this capsule must never contain secrets, tokens, credentials, PII or source code.",
|
|
5484
|
+
""
|
|
5485
|
+
];
|
|
5486
|
+
return parts.join("\n");
|
|
5487
|
+
}
|
|
5488
|
+
function serializeCapsuleJson(c) {
|
|
5489
|
+
return JSON.stringify({ type: "knowledge-capsule", ...c }, null, 2) + "\n";
|
|
5490
|
+
}
|
|
5491
|
+
var EXTERNAL_PATH = ".kaddo/external.yml";
|
|
5492
|
+
function loadExternalRegistry(dir) {
|
|
5493
|
+
const p2 = join(dir, EXTERNAL_PATH);
|
|
5494
|
+
if (!exists(p2)) return [];
|
|
5495
|
+
try {
|
|
5496
|
+
const parsed = parseYaml8(readFile(p2));
|
|
5497
|
+
return Array.isArray(parsed?.external) ? parsed.external : [];
|
|
5498
|
+
} catch {
|
|
5499
|
+
return [];
|
|
5500
|
+
}
|
|
5501
|
+
}
|
|
5502
|
+
function serializeExternalRegistry(entries) {
|
|
5503
|
+
return stringifyYaml3({ external: entries });
|
|
5504
|
+
}
|
|
5505
|
+
function sectionList(md, title) {
|
|
5506
|
+
const re = new RegExp(`^##\\s+${title}\\s*$`, "im");
|
|
5507
|
+
const lines = md.split(/\r?\n/);
|
|
5508
|
+
const start = lines.findIndex((l) => re.test(l));
|
|
5509
|
+
if (start < 0) return [];
|
|
5510
|
+
const out = [];
|
|
5511
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
5512
|
+
if (/^##\s+/.test(lines[i])) break;
|
|
5513
|
+
const m = lines[i].match(/^\s*-\s+(.+?)\s*$/);
|
|
5514
|
+
if (m && !/^_.*_$/.test(m[1])) out.push(m[1].trim());
|
|
5515
|
+
}
|
|
5516
|
+
return out;
|
|
5517
|
+
}
|
|
5518
|
+
function sectionParagraph(md, title) {
|
|
5519
|
+
const re = new RegExp(`^##\\s+${title}\\s*$`, "im");
|
|
5520
|
+
const lines = md.split(/\r?\n/);
|
|
5521
|
+
const start = lines.findIndex((l) => re.test(l));
|
|
5522
|
+
if (start < 0) return "";
|
|
5523
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
5524
|
+
if (/^##\s+/.test(lines[i])) break;
|
|
5525
|
+
const t = lines[i].trim();
|
|
5526
|
+
if (t && !/^_.*_$/.test(t)) return t;
|
|
5527
|
+
}
|
|
5528
|
+
return "";
|
|
5529
|
+
}
|
|
5530
|
+
function parseCapsule(id, path5, md) {
|
|
5531
|
+
const { data } = matter2(md);
|
|
5532
|
+
const updatedAt = data.updated_at ? String(data.updated_at) : void 0;
|
|
5533
|
+
let ageDays = null;
|
|
5534
|
+
if (updatedAt) {
|
|
5535
|
+
const d = Date.parse(updatedAt);
|
|
5536
|
+
if (!Number.isNaN(d)) ageDays = Math.max(0, Math.floor((Date.now() - d) / 864e5));
|
|
5537
|
+
}
|
|
5538
|
+
return {
|
|
5539
|
+
id,
|
|
5540
|
+
path: path5,
|
|
5541
|
+
system: data.system ? String(data.system) : id,
|
|
5542
|
+
owner: data.owner ? String(data.owner) : void 0,
|
|
5543
|
+
updatedAt,
|
|
5544
|
+
purpose: sectionParagraph(md, "Purpose"),
|
|
5545
|
+
capabilities: sectionList(md, "Exposed Capabilities"),
|
|
5546
|
+
contracts: sectionList(md, "Public Contracts"),
|
|
5547
|
+
knownRisks: sectionList(md, "Known Risks"),
|
|
5548
|
+
ageDays
|
|
5549
|
+
};
|
|
5550
|
+
}
|
|
5551
|
+
function slugId(s) {
|
|
5552
|
+
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "external";
|
|
5553
|
+
}
|
|
5554
|
+
function addExternalCapsule(dir, sourceFile) {
|
|
5555
|
+
if (!exists(sourceFile)) throw new Error(`Capsule not found: ${sourceFile}`);
|
|
5556
|
+
const raw = readFile(sourceFile);
|
|
5557
|
+
const { data } = matter2(raw);
|
|
5558
|
+
const isCapsuleType = data.type === "knowledge-capsule";
|
|
5559
|
+
const fallbackName = sourceFile.split(/[\\/]/).pop()?.replace(/\.capsule\.md$/, "") ?? "external";
|
|
5560
|
+
const id = slugId(String(data.system ?? fallbackName));
|
|
5561
|
+
const destRel = join("external", `${id}.capsule.md`).replace(/\\/g, "/");
|
|
5562
|
+
writeFile(join(dir, destRel), raw);
|
|
5563
|
+
const entry = {
|
|
5564
|
+
id,
|
|
5565
|
+
type: "knowledge-capsule",
|
|
5566
|
+
path: destRel,
|
|
5567
|
+
owner: data.owner ? String(data.owner) : void 0,
|
|
5568
|
+
lastImportedAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
5569
|
+
};
|
|
5570
|
+
const registry = loadExternalRegistry(dir).filter((e) => e.id !== id);
|
|
5571
|
+
registry.push(entry);
|
|
5572
|
+
writeFile(join(dir, EXTERNAL_PATH), serializeExternalRegistry(registry));
|
|
5573
|
+
return { id, destRel, entry, isCapsuleType };
|
|
5574
|
+
}
|
|
5575
|
+
function loadExternalCapsules(dir) {
|
|
5576
|
+
const out = [];
|
|
5577
|
+
for (const entry of loadExternalRegistry(dir)) {
|
|
5578
|
+
const full = join(dir, entry.path);
|
|
5579
|
+
if (!exists(full) || !isFile(full)) continue;
|
|
5580
|
+
try {
|
|
5581
|
+
out.push(parseCapsule(entry.id, entry.path, readFile(full)));
|
|
5582
|
+
} catch {
|
|
5583
|
+
}
|
|
5584
|
+
}
|
|
5585
|
+
return out;
|
|
5586
|
+
}
|
|
5587
|
+
|
|
5588
|
+
// src/core/knowledge-discovery.ts
|
|
5589
|
+
var KNOWLEDGE2 = "knowledge";
|
|
5267
5590
|
var CONSOLIDATED_TYPE = {
|
|
5268
5591
|
Business: "business",
|
|
5269
5592
|
Product: "product",
|
|
@@ -5305,10 +5628,10 @@ function layerForType(type) {
|
|
|
5305
5628
|
}
|
|
5306
5629
|
function layerFromPath(filePath) {
|
|
5307
5630
|
const p2 = filePath.replace(/\\/g, "/");
|
|
5308
|
-
if (p2.includes(`/${
|
|
5309
|
-
if (p2.includes(`/${
|
|
5310
|
-
if (p2.includes(`/${
|
|
5311
|
-
if (p2.includes(`/${
|
|
5631
|
+
if (p2.includes(`/${KNOWLEDGE2}/business/`)) return "Business";
|
|
5632
|
+
if (p2.includes(`/${KNOWLEDGE2}/product/`)) return "Product";
|
|
5633
|
+
if (p2.includes(`/${KNOWLEDGE2}/tech/`)) return "Tech";
|
|
5634
|
+
if (p2.includes(`/${KNOWLEDGE2}/delivery/`)) return "Delivery";
|
|
5312
5635
|
return null;
|
|
5313
5636
|
}
|
|
5314
5637
|
function basename(p2) {
|
|
@@ -5321,7 +5644,7 @@ function discoverLayers(dir) {
|
|
|
5321
5644
|
Tech: blank(),
|
|
5322
5645
|
Delivery: blank()
|
|
5323
5646
|
};
|
|
5324
|
-
const archDir = join(dir,
|
|
5647
|
+
const archDir = join(dir, KNOWLEDGE2);
|
|
5325
5648
|
const artifacts = exists(archDir) ? readArtifacts(archDir) : [];
|
|
5326
5649
|
for (const a of artifacts) {
|
|
5327
5650
|
const type = a.type;
|
|
@@ -5343,7 +5666,7 @@ function discoverLayers(dir) {
|
|
|
5343
5666
|
}
|
|
5344
5667
|
if (type === "adr" || type === "decision") slot.hasDecision = true;
|
|
5345
5668
|
}
|
|
5346
|
-
if (existsDirWithMd(join(dir,
|
|
5669
|
+
if (existsDirWithMd(join(dir, KNOWLEDGE2, "tech", "decisions"))) acc.Tech.structured = true;
|
|
5347
5670
|
return ["Business", "Product", "Tech", "Delivery"].map((layer2) => ({
|
|
5348
5671
|
layer: layer2,
|
|
5349
5672
|
status: statusFor(layer2, acc[layer2]),
|
|
@@ -5591,6 +5914,7 @@ function buildProjectExplanation(dir) {
|
|
|
5591
5914
|
ownership,
|
|
5592
5915
|
domains,
|
|
5593
5916
|
duplicateWorkItems,
|
|
5917
|
+
externalCapsules: loadExternalCapsules(dir),
|
|
5594
5918
|
layers,
|
|
5595
5919
|
roadmap,
|
|
5596
5920
|
mappedModules,
|
|
@@ -5719,6 +6043,17 @@ function renderExplanationHuman(exp) {
|
|
|
5719
6043
|
lines.push("- Mapped modules: 0");
|
|
5720
6044
|
lines.push("");
|
|
5721
6045
|
}
|
|
6046
|
+
if (exp.externalCapsules.length > 0) {
|
|
6047
|
+
lines.push(`## External Knowledge Capsules: ${exp.externalCapsules.length}`);
|
|
6048
|
+
for (const cap of exp.externalCapsules) {
|
|
6049
|
+
const owner = cap.owner ? ` \u2014 owner: ${cap.owner}` : "";
|
|
6050
|
+
lines.push(`- ${cap.system}${owner}`);
|
|
6051
|
+
if (cap.ageDays !== null && cap.ageDays >= 90) {
|
|
6052
|
+
lines.push(` \u26A0 capsule last updated ${cap.ageDays} days ago \u2014 it may be stale.`);
|
|
6053
|
+
}
|
|
6054
|
+
}
|
|
6055
|
+
lines.push("");
|
|
6056
|
+
}
|
|
5722
6057
|
if (exp.missingKnowledge.length > 0) {
|
|
5723
6058
|
lines.push("## Missing Knowledge");
|
|
5724
6059
|
for (const m of exp.missingKnowledge) lines.push(`- ${m}`);
|
|
@@ -5762,7 +6097,7 @@ function readKnowledge(dir) {
|
|
|
5762
6097
|
if (!exists(knowledgePath)) return null;
|
|
5763
6098
|
try {
|
|
5764
6099
|
const raw = readFile(knowledgePath);
|
|
5765
|
-
const { data, content } =
|
|
6100
|
+
const { data, content } = matter3(raw);
|
|
5766
6101
|
return { content, data };
|
|
5767
6102
|
} catch {
|
|
5768
6103
|
return null;
|
|
@@ -5773,7 +6108,7 @@ function readRoadmap(dir) {
|
|
|
5773
6108
|
if (!exists(roadmapPath)) return null;
|
|
5774
6109
|
try {
|
|
5775
6110
|
const raw = readFile(roadmapPath);
|
|
5776
|
-
const { content } =
|
|
6111
|
+
const { content } = matter3(raw);
|
|
5777
6112
|
return content.trim();
|
|
5778
6113
|
} catch {
|
|
5779
6114
|
return null;
|
|
@@ -5783,7 +6118,7 @@ function readConfig(dir) {
|
|
|
5783
6118
|
const configPath = join(dir, CONFIG_PATH3);
|
|
5784
6119
|
if (!exists(configPath)) return {};
|
|
5785
6120
|
try {
|
|
5786
|
-
return
|
|
6121
|
+
return parseYaml9(readFile(configPath));
|
|
5787
6122
|
} catch {
|
|
5788
6123
|
return {};
|
|
5789
6124
|
}
|
|
@@ -5869,8 +6204,8 @@ function explainForAgent(dir, artifacts, opts) {
|
|
|
5869
6204
|
since: opts.since ?? null
|
|
5870
6205
|
};
|
|
5871
6206
|
if (knowledge) {
|
|
5872
|
-
const
|
|
5873
|
-
output.knowledge_summary =
|
|
6207
|
+
const firstParagraph3 = knowledge.content.trim().split("\n\n").find((p2) => p2.trim() && !p2.startsWith("#"));
|
|
6208
|
+
output.knowledge_summary = firstParagraph3?.trim() ?? "";
|
|
5874
6209
|
}
|
|
5875
6210
|
const mappedArtifacts = artifacts.filter((a) => a.type !== "current-state" && a.type !== "roadmap").map((a) => ({
|
|
5876
6211
|
id: a.id,
|
|
@@ -5942,7 +6277,7 @@ function runExplain(opts) {
|
|
|
5942
6277
|
}
|
|
5943
6278
|
|
|
5944
6279
|
// src/core/context-pack.ts
|
|
5945
|
-
import
|
|
6280
|
+
import matter4 from "gray-matter";
|
|
5946
6281
|
var CONTEXT_PACK_VERSION = "1";
|
|
5947
6282
|
var ARCH_DIR6 = "knowledge";
|
|
5948
6283
|
function readScanJson(dir) {
|
|
@@ -5954,8 +6289,8 @@ function readScanJson(dir) {
|
|
|
5954
6289
|
return null;
|
|
5955
6290
|
}
|
|
5956
6291
|
}
|
|
5957
|
-
function
|
|
5958
|
-
const body =
|
|
6292
|
+
function firstParagraph2(markdown) {
|
|
6293
|
+
const body = matter4(markdown).content.trim();
|
|
5959
6294
|
const para = body.split("\n\n").map((p2) => p2.trim()).find((p2) => p2 && !p2.startsWith("#"));
|
|
5960
6295
|
return para ?? "";
|
|
5961
6296
|
}
|
|
@@ -5963,7 +6298,7 @@ function readMarkdownSummary(dir, file) {
|
|
|
5963
6298
|
const p2 = join(dir, ARCH_DIR6, file);
|
|
5964
6299
|
if (!exists(p2)) return null;
|
|
5965
6300
|
try {
|
|
5966
|
-
return
|
|
6301
|
+
return firstParagraph2(readFile(p2));
|
|
5967
6302
|
} catch {
|
|
5968
6303
|
return null;
|
|
5969
6304
|
}
|
|
@@ -6125,6 +6460,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
6125
6460
|
roadmap,
|
|
6126
6461
|
phase,
|
|
6127
6462
|
deliveryMix,
|
|
6463
|
+
external: loadExternalCapsules(dir),
|
|
6128
6464
|
mappedModules,
|
|
6129
6465
|
missing,
|
|
6130
6466
|
// VS-052: the handoff is driven by the REAL phase, not project.state, so the pack never
|
|
@@ -6275,6 +6611,21 @@ function renderContextPack(pack) {
|
|
|
6275
6611
|
"Note: Kaddo does not scan secondary repositories during `context`. Mapped modules come from `.kaddo/modules.yml` and module artifacts only.\n"
|
|
6276
6612
|
);
|
|
6277
6613
|
}
|
|
6614
|
+
if (pack.external.length > 0) {
|
|
6615
|
+
parts.push("## External Knowledge\n");
|
|
6616
|
+
parts.push(
|
|
6617
|
+
"Imported Knowledge Capsules \u2014 minimal context about external systems (not their source).\n"
|
|
6618
|
+
);
|
|
6619
|
+
for (const cap of pack.external) {
|
|
6620
|
+
const lines = [`### ${cap.system}`, ""];
|
|
6621
|
+
if (cap.purpose) lines.push(`- Purpose: ${cap.purpose}`);
|
|
6622
|
+
if (cap.capabilities.length) lines.push(`- Capabilities: ${cap.capabilities.join(", ")}`);
|
|
6623
|
+
if (cap.contracts.length) lines.push(`- Contracts: ${cap.contracts.join(", ")}`);
|
|
6624
|
+
if (cap.owner) lines.push(`- Owner: ${cap.owner}`);
|
|
6625
|
+
if (cap.knownRisks.length) lines.push(`- Known risks: ${cap.knownRisks.join("; ")}`);
|
|
6626
|
+
parts.push(lines.join("\n") + "\n");
|
|
6627
|
+
}
|
|
6628
|
+
}
|
|
6278
6629
|
parts.push("## Missing Context\n");
|
|
6279
6630
|
if (missing.length > 0) {
|
|
6280
6631
|
parts.push(missing.map((m) => `- ${m}`).join("\n") + "\n");
|
|
@@ -6490,7 +6841,7 @@ function renderUnderstandTerminal(plan) {
|
|
|
6490
6841
|
}
|
|
6491
6842
|
|
|
6492
6843
|
// src/core/delivery.ts
|
|
6493
|
-
import { parse as
|
|
6844
|
+
import { parse as parseYaml10 } from "yaml";
|
|
6494
6845
|
function slugify2(s) {
|
|
6495
6846
|
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
6496
6847
|
}
|
|
@@ -6596,6 +6947,14 @@ function runUnderstand() {
|
|
|
6596
6947
|
if (assessment.nextStep) {
|
|
6597
6948
|
console.log(`Next step: ${assessment.nextStep}`);
|
|
6598
6949
|
}
|
|
6950
|
+
if (exp.externalCapsules.length > 0) {
|
|
6951
|
+
console.log("");
|
|
6952
|
+
console.log("External knowledge:");
|
|
6953
|
+
for (const cap of exp.externalCapsules) {
|
|
6954
|
+
console.log(` - ${cap.system}${cap.owner ? ` (owner: ${cap.owner})` : ""}`);
|
|
6955
|
+
}
|
|
6956
|
+
console.log(" \u2192 Review the relevant capsule before changing integration behavior with it.");
|
|
6957
|
+
}
|
|
6599
6958
|
const active = activeWorkItems(dir);
|
|
6600
6959
|
if (active.length > 0) {
|
|
6601
6960
|
console.log("");
|
|
@@ -6829,14 +7188,14 @@ async function runClassify(opts = {}) {
|
|
|
6829
7188
|
}
|
|
6830
7189
|
|
|
6831
7190
|
// src/commands/status.ts
|
|
6832
|
-
import { parse as
|
|
7191
|
+
import { parse as parseYaml11 } from "yaml";
|
|
6833
7192
|
var ARCH_DIR8 = "knowledge";
|
|
6834
7193
|
var CONFIG_PATH4 = ".kaddo/config.yml";
|
|
6835
7194
|
function loadConfig3(dir) {
|
|
6836
7195
|
const p2 = join(dir, CONFIG_PATH4);
|
|
6837
7196
|
if (!exists(p2)) return {};
|
|
6838
7197
|
try {
|
|
6839
|
-
return
|
|
7198
|
+
return parseYaml11(readFile(p2));
|
|
6840
7199
|
} catch {
|
|
6841
7200
|
return {};
|
|
6842
7201
|
}
|
|
@@ -6893,7 +7252,7 @@ function runStatus() {
|
|
|
6893
7252
|
}
|
|
6894
7253
|
|
|
6895
7254
|
// src/commands/learn.ts
|
|
6896
|
-
import
|
|
7255
|
+
import matter5 from "gray-matter";
|
|
6897
7256
|
var ARCH_DIR9 = "knowledge";
|
|
6898
7257
|
var WORK_ITEMS_DIR2 = "knowledge/delivery/work-items";
|
|
6899
7258
|
function findWorkItemFile(dir, id) {
|
|
@@ -6905,7 +7264,7 @@ function findWorkItemFile(dir, id) {
|
|
|
6905
7264
|
}
|
|
6906
7265
|
function updateWorkItemFile(filePath, learning) {
|
|
6907
7266
|
const raw = readFile(filePath);
|
|
6908
|
-
const { data, content } =
|
|
7267
|
+
const { data, content } = matter5(raw);
|
|
6909
7268
|
data.status = "done";
|
|
6910
7269
|
data.completed_at = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
6911
7270
|
let updatedContent = content;
|
|
@@ -6930,7 +7289,7 @@ ${learning.trim()}
|
|
|
6930
7289
|
${learning.trim()}
|
|
6931
7290
|
`;
|
|
6932
7291
|
}
|
|
6933
|
-
const newRaw =
|
|
7292
|
+
const newRaw = matter5.stringify(updatedContent, data);
|
|
6934
7293
|
writeFile(filePath, newRaw);
|
|
6935
7294
|
}
|
|
6936
7295
|
async function runLearn(artifactId) {
|
|
@@ -7038,13 +7397,13 @@ function runHistory(opts = {}) {
|
|
|
7038
7397
|
}
|
|
7039
7398
|
|
|
7040
7399
|
// src/commands/add.ts
|
|
7041
|
-
import { parse as
|
|
7400
|
+
import { parse as parseYaml12, stringify as stringifyYaml4 } from "yaml";
|
|
7042
7401
|
var CONFIG_PATH5 = ".kaddo/config.yml";
|
|
7043
7402
|
function readProjectState(dir) {
|
|
7044
7403
|
const configPath = join(dir, CONFIG_PATH5);
|
|
7045
7404
|
if (!exists(configPath)) return void 0;
|
|
7046
7405
|
try {
|
|
7047
|
-
const config =
|
|
7406
|
+
const config = parseYaml12(readFile(configPath));
|
|
7048
7407
|
return config.project?.state;
|
|
7049
7408
|
} catch {
|
|
7050
7409
|
return void 0;
|
|
@@ -7062,12 +7421,12 @@ function markModuleInstalled(dir, configKey, moduleName) {
|
|
|
7062
7421
|
const configPath = join(dir, CONFIG_PATH5);
|
|
7063
7422
|
if (!exists(configPath)) return;
|
|
7064
7423
|
try {
|
|
7065
|
-
const config =
|
|
7424
|
+
const config = parseYaml12(readFile(configPath));
|
|
7066
7425
|
config[configKey] = { installed: true, installed_at: (/* @__PURE__ */ new Date()).toISOString().split("T")[0] };
|
|
7067
7426
|
const modules = config.modules ?? [];
|
|
7068
7427
|
if (!modules.includes(moduleName)) modules.push(moduleName);
|
|
7069
7428
|
config.modules = modules;
|
|
7070
|
-
writeFile(configPath,
|
|
7429
|
+
writeFile(configPath, stringifyYaml4(config));
|
|
7071
7430
|
} catch {
|
|
7072
7431
|
}
|
|
7073
7432
|
}
|
|
@@ -7075,7 +7434,7 @@ function isModuleInstalled(dir, configKey) {
|
|
|
7075
7434
|
const configPath = join(dir, CONFIG_PATH5);
|
|
7076
7435
|
if (!exists(configPath)) return false;
|
|
7077
7436
|
try {
|
|
7078
|
-
const config =
|
|
7437
|
+
const config = parseYaml12(readFile(configPath));
|
|
7079
7438
|
const moduleConfig = config[configKey];
|
|
7080
7439
|
return moduleConfig?.installed === true;
|
|
7081
7440
|
} catch {
|
|
@@ -7158,7 +7517,7 @@ function runAdd(moduleName, opts = {}, dir = cwd()) {
|
|
|
7158
7517
|
}
|
|
7159
7518
|
|
|
7160
7519
|
// src/core/ownership-suggest.ts
|
|
7161
|
-
import
|
|
7520
|
+
import matter6 from "gray-matter";
|
|
7162
7521
|
var SCAN_PATH = ".kaddo/scan.json";
|
|
7163
7522
|
function normalizeGlob(input) {
|
|
7164
7523
|
let g = input.trim().replace(/\\/g, "/");
|
|
@@ -7280,11 +7639,11 @@ function suggestGlobs(artifact, signals) {
|
|
|
7280
7639
|
return [...new Set(out)];
|
|
7281
7640
|
}
|
|
7282
7641
|
function applyOwnership(raw, globs, mode = "replace") {
|
|
7283
|
-
const parsed =
|
|
7642
|
+
const parsed = matter6(raw);
|
|
7284
7643
|
const existing = toStringArray3(parsed.data.code);
|
|
7285
7644
|
const next = mode === "append" ? [.../* @__PURE__ */ new Set([...existing, ...globs])] : [...new Set(globs)];
|
|
7286
7645
|
const data = { ...parsed.data, code: next };
|
|
7287
|
-
return
|
|
7646
|
+
return matter6.stringify(parsed.content, data);
|
|
7288
7647
|
}
|
|
7289
7648
|
|
|
7290
7649
|
// src/commands/owners.ts
|
|
@@ -7458,19 +7817,19 @@ ${globs.map((g) => ` - ${g}`).join("\n")}`);
|
|
|
7458
7817
|
}
|
|
7459
7818
|
|
|
7460
7819
|
// src/commands/module-descriptor.ts
|
|
7461
|
-
import { parse as
|
|
7820
|
+
import { parse as parseYaml13, stringify as stringifyYaml5 } from "yaml";
|
|
7462
7821
|
var DESCRIPTOR_PATH2 = "knowledge/module.yml";
|
|
7463
7822
|
function readDescriptor(dir) {
|
|
7464
7823
|
const path5 = join(dir, DESCRIPTOR_PATH2);
|
|
7465
7824
|
if (!exists(path5)) return null;
|
|
7466
7825
|
try {
|
|
7467
|
-
return
|
|
7826
|
+
return parseYaml13(readFile(path5));
|
|
7468
7827
|
} catch {
|
|
7469
7828
|
return null;
|
|
7470
7829
|
}
|
|
7471
7830
|
}
|
|
7472
7831
|
function writeDescriptor(dir, descriptor) {
|
|
7473
|
-
writeFile(join(dir, DESCRIPTOR_PATH2),
|
|
7832
|
+
writeFile(join(dir, DESCRIPTOR_PATH2), stringifyYaml5(descriptor));
|
|
7474
7833
|
}
|
|
7475
7834
|
async function runModuleDescriptor(opts) {
|
|
7476
7835
|
const dir = cwd();
|
|
@@ -7565,7 +7924,7 @@ function printDescriptor(d) {
|
|
|
7565
7924
|
}
|
|
7566
7925
|
|
|
7567
7926
|
// src/commands/modules-map.ts
|
|
7568
|
-
import { parse as
|
|
7927
|
+
import { parse as parseYaml14, stringify as stringifyYaml6 } from "yaml";
|
|
7569
7928
|
|
|
7570
7929
|
// src/templates/registry.ts
|
|
7571
7930
|
var QUALITY = "## Quality checklist";
|
|
@@ -7708,7 +8067,7 @@ ${QUALITY}
|
|
|
7708
8067
|
- [ ] Capabilities describe outcomes, not implementation.
|
|
7709
8068
|
- [ ] Each capability cites evidence or is flagged as an assumption.
|
|
7710
8069
|
`;
|
|
7711
|
-
var
|
|
8070
|
+
var KNOWLEDGE3 = `---
|
|
7712
8071
|
type: current-state
|
|
7713
8072
|
updated_at: YYYY-MM-DD
|
|
7714
8073
|
---
|
|
@@ -8718,7 +9077,7 @@ var KADDO_TEMPLATES = [
|
|
|
8718
9077
|
description: "What is true about the product right now.",
|
|
8719
9078
|
whenToUse: "Created by `kaddo init`; keep it current as the product evolves.",
|
|
8720
9079
|
relatedCommand: "kaddo init",
|
|
8721
|
-
content:
|
|
9080
|
+
content: KNOWLEDGE3
|
|
8722
9081
|
},
|
|
8723
9082
|
// business / product (bootstrap — consolidated, minimal)
|
|
8724
9083
|
{
|
|
@@ -9072,14 +9431,14 @@ function readModulesDescriptor(dir) {
|
|
|
9072
9431
|
const path5 = join(dir, DESCRIPTOR_PATH3);
|
|
9073
9432
|
if (!exists(path5)) return { version: 1, modules: [] };
|
|
9074
9433
|
try {
|
|
9075
|
-
const parsed =
|
|
9434
|
+
const parsed = parseYaml14(readFile(path5));
|
|
9076
9435
|
return { version: parsed.version ?? 1, modules: parsed.modules ?? [] };
|
|
9077
9436
|
} catch {
|
|
9078
9437
|
return { version: 1, modules: [] };
|
|
9079
9438
|
}
|
|
9080
9439
|
}
|
|
9081
9440
|
function writeModulesDescriptor(dir, descriptor) {
|
|
9082
|
-
writeFile(join(dir, DESCRIPTOR_PATH3),
|
|
9441
|
+
writeFile(join(dir, DESCRIPTOR_PATH3), stringifyYaml6(descriptor));
|
|
9083
9442
|
}
|
|
9084
9443
|
function moduleDir(id) {
|
|
9085
9444
|
return `knowledge/tech/modules/${id}`;
|
|
@@ -9369,6 +9728,51 @@ async function runBootstrap(dir = cwd()) {
|
|
|
9369
9728
|
);
|
|
9370
9729
|
}
|
|
9371
9730
|
|
|
9731
|
+
// src/commands/capsule.ts
|
|
9732
|
+
function slug(s) {
|
|
9733
|
+
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
9734
|
+
}
|
|
9735
|
+
function runCapsuleExport() {
|
|
9736
|
+
const dir = cwd();
|
|
9737
|
+
const config = requireConfig(dir);
|
|
9738
|
+
intro2("kaddo capsule export");
|
|
9739
|
+
const capsule = buildCapsule(dir, config);
|
|
9740
|
+
const name = slug(config.project.name);
|
|
9741
|
+
const mdPath = join(".kaddo", "exports", `${name}.capsule.md`);
|
|
9742
|
+
const jsonPath = join(".kaddo", "exports", `${name}.capsule.json`);
|
|
9743
|
+
writeFile(join(dir, mdPath), renderCapsuleMarkdown(capsule));
|
|
9744
|
+
writeFile(join(dir, jsonPath), serializeCapsuleJson(capsule));
|
|
9745
|
+
log2.success(`Wrote ${mdPath}`);
|
|
9746
|
+
log2.success(`Wrote ${jsonPath}`);
|
|
9747
|
+
log2.info("Refine it with the capsule-agent before sharing \u2014 and never include secrets or source code.");
|
|
9748
|
+
printCommandFooter("capsule export");
|
|
9749
|
+
outro2("Knowledge Capsule exported.");
|
|
9750
|
+
}
|
|
9751
|
+
function runCapsuleAdd(srcPath) {
|
|
9752
|
+
const dir = cwd();
|
|
9753
|
+
requireConfig(dir);
|
|
9754
|
+
intro2("kaddo capsule add");
|
|
9755
|
+
if (!srcPath) {
|
|
9756
|
+
console.error("Usage: kaddo capsule add <path-to-capsule.md>");
|
|
9757
|
+
process.exit(1);
|
|
9758
|
+
}
|
|
9759
|
+
const abs = join(dir, srcPath);
|
|
9760
|
+
const source = exists(abs) ? abs : srcPath;
|
|
9761
|
+
if (!exists(source)) {
|
|
9762
|
+
console.error(`Capsule not found: ${srcPath}`);
|
|
9763
|
+
process.exit(1);
|
|
9764
|
+
}
|
|
9765
|
+
const result = addExternalCapsule(dir, source);
|
|
9766
|
+
if (!result.isCapsuleType) {
|
|
9767
|
+
log2.warn("This file is not marked as a knowledge-capsule (front matter `type: knowledge-capsule`).");
|
|
9768
|
+
}
|
|
9769
|
+
log2.success(`Imported capsule "${result.id}" \u2192 ${result.destRel}`);
|
|
9770
|
+
log2.success("Registered in .kaddo/external.yml");
|
|
9771
|
+
log2.info('Run `kaddo context` \u2014 the pack now includes an "External Knowledge" section.');
|
|
9772
|
+
printCommandFooter("capsule add");
|
|
9773
|
+
outro2("External capsule registered.");
|
|
9774
|
+
}
|
|
9775
|
+
|
|
9372
9776
|
// src/index.ts
|
|
9373
9777
|
var require2 = createRequire(import.meta.url);
|
|
9374
9778
|
var { version } = require2("../package.json");
|
|
@@ -9386,6 +9790,13 @@ program.command("bootstrap").description("Build the initial knowledge base for a
|
|
|
9386
9790
|
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) => {
|
|
9387
9791
|
await runCreate(type ?? "", opts);
|
|
9388
9792
|
});
|
|
9793
|
+
var capsuleCmd = program.command("capsule").description("Export this project as a Knowledge Capsule, or import an external one as context");
|
|
9794
|
+
capsuleCmd.command("export").description("Write a Knowledge Capsule about this project to .kaddo/exports/").action(() => {
|
|
9795
|
+
runCapsuleExport();
|
|
9796
|
+
});
|
|
9797
|
+
capsuleCmd.command("add <path>").description("Register an external Knowledge Capsule as project context (.kaddo/external.yml)").action((path5) => {
|
|
9798
|
+
runCapsuleAdd(path5);
|
|
9799
|
+
});
|
|
9389
9800
|
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) => {
|
|
9390
9801
|
await runGuard(opts);
|
|
9391
9802
|
});
|