@kaddo/mcp 3.117.0 → 3.119.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/index.js +544 -98
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -389,7 +389,7 @@ function join(...parts) {
|
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
// ../cli/src/core/impact-report.ts
|
|
392
|
-
import
|
|
392
|
+
import matter11 from "gray-matter";
|
|
393
393
|
|
|
394
394
|
// ../cli/src/core/config.ts
|
|
395
395
|
import { z } from "zod";
|
|
@@ -691,12 +691,12 @@ function classifyLayer(posixPath) {
|
|
|
691
691
|
return "unknown";
|
|
692
692
|
}
|
|
693
693
|
function enrich(dir, a2) {
|
|
694
|
-
const
|
|
694
|
+
const posix3 = toPosix2(a2.filePath);
|
|
695
695
|
const isWi = isWorkItemArtifact(a2);
|
|
696
696
|
return {
|
|
697
697
|
...a2,
|
|
698
698
|
relPath: relativeTo(dir, a2.filePath),
|
|
699
|
-
layer: classifyLayer(
|
|
699
|
+
layer: classifyLayer(posix3),
|
|
700
700
|
isWorkItem: isWi,
|
|
701
701
|
lifecycle: isWi ? lifecycleStateOf({ status: a2.status, filePath: a2.filePath }) : void 0
|
|
702
702
|
};
|
|
@@ -3914,8 +3914,8 @@ function buildOpenQuestionsReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
3914
3914
|
const project = loadConfig(dir)?.project.name ?? "unknown";
|
|
3915
3915
|
const questions = [];
|
|
3916
3916
|
let seq = 0;
|
|
3917
|
-
for (const
|
|
3918
|
-
const p = join(dir,
|
|
3917
|
+
for (const source3 of SOURCES) {
|
|
3918
|
+
const p = join(dir, source3);
|
|
3919
3919
|
if (!exists(p)) continue;
|
|
3920
3920
|
let md;
|
|
3921
3921
|
try {
|
|
@@ -3928,8 +3928,8 @@ function buildOpenQuestionsReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
3928
3928
|
const { classification, reason } = classifyQuestion(eq.text);
|
|
3929
3929
|
questions.push({
|
|
3930
3930
|
id: `OQ-${String(seq).padStart(3, "0")}`,
|
|
3931
|
-
source,
|
|
3932
|
-
sourcePath:
|
|
3931
|
+
source: source3,
|
|
3932
|
+
sourcePath: source3,
|
|
3933
3933
|
line: eq.line,
|
|
3934
3934
|
raw: eq.raw,
|
|
3935
3935
|
section: "Open Questions",
|
|
@@ -4360,6 +4360,121 @@ function readPocSummary(dir) {
|
|
|
4360
4360
|
};
|
|
4361
4361
|
}
|
|
4362
4362
|
|
|
4363
|
+
// ../cli/src/core/poc-report.ts
|
|
4364
|
+
import { createHash } from "crypto";
|
|
4365
|
+
import matter8 from "gray-matter";
|
|
4366
|
+
var POC_REPORT_PREFIX = "poc-report-v";
|
|
4367
|
+
var SECRET_LINE = /(^|\s)(?:[A-Z][A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE_KEY)|(?:token|secret|password|api[_-]?key))\s*[:=]\s*\S+/i;
|
|
4368
|
+
var reportName = /^poc-report-v(\d{3,})\.md$/;
|
|
4369
|
+
function sha(value) {
|
|
4370
|
+
return createHash("sha256").update(value).digest("hex");
|
|
4371
|
+
}
|
|
4372
|
+
function posix(value) {
|
|
4373
|
+
return value.replace(/\\/g, "/");
|
|
4374
|
+
}
|
|
4375
|
+
function redactSecrets(value) {
|
|
4376
|
+
return value.split(/\r?\n/).map((line) => {
|
|
4377
|
+
if (!SECRET_LINE.test(line)) return line;
|
|
4378
|
+
return line.replace(/([:=]\s*)\S+.*/, "$1[redacted]");
|
|
4379
|
+
}).join("\n");
|
|
4380
|
+
}
|
|
4381
|
+
function source(dir, path9, kind) {
|
|
4382
|
+
const absolute = join(dir, path9);
|
|
4383
|
+
if (!exists(absolute)) return null;
|
|
4384
|
+
const raw = readFile(absolute);
|
|
4385
|
+
return { path: posix(path9), kind, digest: sha(raw), content: redactSecrets(raw) };
|
|
4386
|
+
}
|
|
4387
|
+
function reportFiles(dir) {
|
|
4388
|
+
const delivery = join(dir, "knowledge", "delivery");
|
|
4389
|
+
return readDir(delivery).flatMap((name) => {
|
|
4390
|
+
const match = name.match(reportName);
|
|
4391
|
+
if (!match) return [];
|
|
4392
|
+
try {
|
|
4393
|
+
const parsed = matter8(readFile(join(delivery, name)));
|
|
4394
|
+
const fingerprint = typeof parsed.data.source_fingerprint === "string" ? parsed.data.source_fingerprint : "";
|
|
4395
|
+
const manifest = parsed.data.source_manifest;
|
|
4396
|
+
const sourceManifest = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? Object.fromEntries(Object.entries(manifest).filter(([, digest]) => typeof digest === "string")) : {};
|
|
4397
|
+
return [{ path: `knowledge/delivery/${name}`, version: Number(match[1]), sourceFingerprint: fingerprint, sourceManifest }];
|
|
4398
|
+
} catch {
|
|
4399
|
+
return [];
|
|
4400
|
+
}
|
|
4401
|
+
}).sort((a2, b2) => b2.version - a2.version);
|
|
4402
|
+
}
|
|
4403
|
+
function reportPath(version) {
|
|
4404
|
+
return `knowledge/delivery/${POC_REPORT_PREFIX}${String(version).padStart(3, "0")}.md`;
|
|
4405
|
+
}
|
|
4406
|
+
function buildPocReportContext(dir) {
|
|
4407
|
+
const poc = readPocSummary(dir);
|
|
4408
|
+
const reports = reportFiles(dir);
|
|
4409
|
+
const latestReport = reports[0] ?? null;
|
|
4410
|
+
const nextPath = reportPath((latestReport?.version ?? 0) + 1);
|
|
4411
|
+
if (!poc.exists || poc.conclusion === "pending") {
|
|
4412
|
+
return {
|
|
4413
|
+
eligible: false,
|
|
4414
|
+
conclusion: poc.conclusion,
|
|
4415
|
+
status: latestReport ? "stale" : "missing",
|
|
4416
|
+
latestReport,
|
|
4417
|
+
nextPath,
|
|
4418
|
+
sourceFingerprint: "",
|
|
4419
|
+
sources: [],
|
|
4420
|
+
changesSinceLatest: [],
|
|
4421
|
+
handoff: "POC conclusion is still pending. Record validated, rejected, or inconclusive before generating a final report."
|
|
4422
|
+
};
|
|
4423
|
+
}
|
|
4424
|
+
const sources = [];
|
|
4425
|
+
const add = (path9, kind) => {
|
|
4426
|
+
if (sources.some((item2) => item2.path === posix(path9))) return;
|
|
4427
|
+
const item = source(dir, path9, kind);
|
|
4428
|
+
if (item) sources.push(item);
|
|
4429
|
+
};
|
|
4430
|
+
add(POC_ARTIFACT_PATH, "poc");
|
|
4431
|
+
const pocText = readFile(join(dir, POC_ARTIFACT_PATH));
|
|
4432
|
+
const knowledge = discoverKnowledge(dir);
|
|
4433
|
+
const workItems = discoverWorkItems(dir);
|
|
4434
|
+
const referencedWorkItems = workItems.filter((wi) => wi.lifecycle === "completed" && wi.id && new RegExp(`\\b${wi.id}\\b`).test(pocText));
|
|
4435
|
+
const resourceIds = /* @__PURE__ */ new Set();
|
|
4436
|
+
const decisionIds = /* @__PURE__ */ new Set();
|
|
4437
|
+
for (const wi of referencedWorkItems) {
|
|
4438
|
+
add(wi.relPath, "work-item");
|
|
4439
|
+
for (const resource of parseWorkItemResources(wi.rawFrontmatter)) resourceIds.add(resource.id);
|
|
4440
|
+
const decisions = wi.rawFrontmatter.decisions;
|
|
4441
|
+
if (Array.isArray(decisions)) decisions.forEach((id) => decisionIds.add(String(id)));
|
|
4442
|
+
}
|
|
4443
|
+
for (const id of resourceIds) {
|
|
4444
|
+
const resource = getResource(dir, id);
|
|
4445
|
+
if (resource) add(resource.path, "resource");
|
|
4446
|
+
}
|
|
4447
|
+
for (const artifact of knowledge) {
|
|
4448
|
+
if (artifact.isWorkItem || !artifact.id) continue;
|
|
4449
|
+
if (decisionIds.has(artifact.id) || referencedWorkItems.some((wi) => new RegExp(`\\b${artifact.id}\\b`).test(readFile(wi.filePath)))) add(artifact.relPath, "knowledge");
|
|
4450
|
+
}
|
|
4451
|
+
sources.sort((a2, b2) => a2.path.localeCompare(b2.path));
|
|
4452
|
+
const sourceFingerprint = sha(sources.map((item) => `${item.path}\0${item.digest}`).join("\n"));
|
|
4453
|
+
const status = !latestReport ? "missing" : latestReport.sourceFingerprint === sourceFingerprint ? "current" : "stale";
|
|
4454
|
+
const currentManifest = Object.fromEntries(sources.map((item) => [item.path, item.digest]));
|
|
4455
|
+
const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path9) => currentManifest[path9] !== latestReport?.sourceManifest[path9]).sort() : [];
|
|
4456
|
+
const sourceList = sources.map((item) => `- ${item.path} (${item.kind})`).join("\n") || "- No sources selected.";
|
|
4457
|
+
return {
|
|
4458
|
+
eligible: true,
|
|
4459
|
+
conclusion: poc.conclusion,
|
|
4460
|
+
status,
|
|
4461
|
+
latestReport,
|
|
4462
|
+
nextPath,
|
|
4463
|
+
sourceFingerprint,
|
|
4464
|
+
sources,
|
|
4465
|
+
changesSinceLatest,
|
|
4466
|
+
handoff: [
|
|
4467
|
+
"Create a POC Final Report from the supplied sources only. Do not explore the repository.",
|
|
4468
|
+
`Canonical conclusion: ${poc.conclusion}. Do not contradict it.`,
|
|
4469
|
+
"Use the 15 canonical sections. Mark missing evidence as Not evaluated, Not applicable, or No evidence available.",
|
|
4470
|
+
"Distinguish measured, calculated, estimated, and projected values. Never include secret values.",
|
|
4471
|
+
"",
|
|
4472
|
+
"Selected sources:",
|
|
4473
|
+
sourceList
|
|
4474
|
+
].join("\n")
|
|
4475
|
+
};
|
|
4476
|
+
}
|
|
4477
|
+
|
|
4363
4478
|
// ../cli/src/core/next-step.ts
|
|
4364
4479
|
function buildDeliveryState(dir) {
|
|
4365
4480
|
const wis = discoverWorkItems(dir);
|
|
@@ -4450,7 +4565,7 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
4450
4565
|
if (projectMode(config) === "poc") {
|
|
4451
4566
|
const poc = readPocSummary(dir);
|
|
4452
4567
|
if (!poc.exists) {
|
|
4453
|
-
return { id: "
|
|
4568
|
+
return { id: "poc-artifact", phase: "POC Definition", label: "Create or restore the POC artifact.", command: "kaddo project mode poc", target: POC_ARTIFACT_PATH, reason: `Missing ${POC_ARTIFACT_PATH}.` };
|
|
4454
4569
|
}
|
|
4455
4570
|
if (!poc.hypothesisDefined) {
|
|
4456
4571
|
return { id: "poc-hypothesis", phase: "POC Definition", label: "Define the POC hypothesis.", target: POC_ARTIFACT_PATH, reason: "The POC needs a testable hypothesis before work begins." };
|
|
@@ -4460,12 +4575,9 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
4460
4575
|
}
|
|
4461
4576
|
const qCurrentState2 = analyzeKnowledgeArtifact(dir, CS);
|
|
4462
4577
|
const qCodebase2 = analyzeKnowledgeArtifact(dir, CB);
|
|
4463
|
-
if (qCurrentState2 !== "useful" || qCodebase2 !== "useful") {
|
|
4578
|
+
if (exists(join(dir, ".kaddo", "scan.json")) && (qCurrentState2 !== "useful" || qCodebase2 !== "useful")) {
|
|
4464
4579
|
return { id: "poc-technical-context", phase: "POC Discovery", label: "Document the minimal technical context for the experiment.", target: qCurrentState2 !== "useful" ? CS : CB, agent: "architecture-agent", reason: "The experiment surface and constraints are not yet sufficiently documented." };
|
|
4465
4580
|
}
|
|
4466
|
-
if (!exists(join(dir, "knowledge", "resources"))) {
|
|
4467
|
-
return { id: "poc-resources", phase: "POC Discovery", label: "Identify the project resources relevant to the experiment.", command: "kaddo resources list", reason: "No project resource catalog is available for the experiment context." };
|
|
4468
|
-
}
|
|
4469
4581
|
const delivery = buildDeliveryState(dir);
|
|
4470
4582
|
if (delivery.total_work_items === 0) {
|
|
4471
4583
|
return { id: "poc-create-experiment", phase: "Experiment", label: "Create the first experiment Work Item (prefer `spike`).", command: "kaddo create spike", reason: "The POC has no Work Item to produce evidence." };
|
|
@@ -4479,7 +4591,11 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
4479
4591
|
if (poc.conclusion === "pending") {
|
|
4480
4592
|
return { id: "poc-evaluate", phase: "Evaluation", label: "Evaluate the experiment evidence and record the POC conclusion.", target: POC_ARTIFACT_PATH, reason: "Experiment Work Items are complete but the POC conclusion is pending." };
|
|
4481
4593
|
}
|
|
4482
|
-
|
|
4594
|
+
const report = buildPocReportContext(dir);
|
|
4595
|
+
if (report.status !== "current") {
|
|
4596
|
+
return { id: "poc-final-report", phase: "Conclusion", label: `POC concluded: ${poc.conclusion}. Generate a final report if it would help communicate the result.`, command: "kaddo poc report", target: POC_ARTIFACT_PATH, reason: report.status === "stale" ? "The latest final report is stale after source changes." : "A final report is optional and has not been generated." };
|
|
4597
|
+
}
|
|
4598
|
+
return { id: "poc-complete", phase: "Conclusion", label: `POC concluded: ${poc.conclusion}. Decide whether to graduate it to standard delivery.`, target: POC_ARTIFACT_PATH, reason: "The POC conclusion and final report are current." };
|
|
4483
4599
|
}
|
|
4484
4600
|
const q3 = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
4485
4601
|
const resolveAgent = (agent) => {
|
|
@@ -4917,7 +5033,7 @@ function deriveDeliveryCompletedStatus(dir) {
|
|
|
4917
5033
|
}
|
|
4918
5034
|
|
|
4919
5035
|
// ../cli/src/core/assets.ts
|
|
4920
|
-
import
|
|
5036
|
+
import matter9 from "gray-matter";
|
|
4921
5037
|
|
|
4922
5038
|
// ../cli/src/core/version.ts
|
|
4923
5039
|
import { fileURLToPath } from "url";
|
|
@@ -5593,6 +5709,50 @@ their intersection with the affected areas, and recommended actions.
|
|
|
5593
5709
|
- The assessment is proportional \u2014 a small change gets a brief assessment.
|
|
5594
5710
|
`
|
|
5595
5711
|
);
|
|
5712
|
+
var POC_REPORT_WRITING = skill(
|
|
5713
|
+
"poc-report-writing",
|
|
5714
|
+
"POC Final Report Writing Skill",
|
|
5715
|
+
"delivery",
|
|
5716
|
+
["implementation-agent", "work-item-agent"],
|
|
5717
|
+
`
|
|
5718
|
+
# POC Final Report Writing Skill
|
|
5719
|
+
|
|
5720
|
+
## Purpose
|
|
5721
|
+
|
|
5722
|
+
Turn a supplied deterministic POC report context into a concise, traceable final report.
|
|
5723
|
+
|
|
5724
|
+
## When to use
|
|
5725
|
+
|
|
5726
|
+
After a POC has a canonical conclusion and Core has prepared its deterministic report context.
|
|
5727
|
+
|
|
5728
|
+
## Rules
|
|
5729
|
+
|
|
5730
|
+
- Use only the supplied context; do not scan the repository or infer missing results.
|
|
5731
|
+
- Treat \`poc.md\` as canonical for the conclusion. Never contradict it.
|
|
5732
|
+
- Keep facts, measurements, calculations, estimates, and recommendations distinct.
|
|
5733
|
+
- Never include credential values, tokens, private keys, passwords, or connection strings.
|
|
5734
|
+
- Use \`Not evaluated in this POC.\`, \`Not applicable.\`, or \`No evidence available.\` when sources do not support a section.
|
|
5735
|
+
- Return a proposal for human review; Core persists it only after confirmation.
|
|
5736
|
+
|
|
5737
|
+
## Required structure
|
|
5738
|
+
|
|
5739
|
+
1. Executive Summary
|
|
5740
|
+
2. Problem and Objective
|
|
5741
|
+
3. Hypothesis and Success Criteria
|
|
5742
|
+
4. Scope, Constraints and Non-goals
|
|
5743
|
+
5. Technical Approach and Architecture
|
|
5744
|
+
6. Infrastructure and Project Resources
|
|
5745
|
+
7. Implementation and Relevant Technical Decisions
|
|
5746
|
+
8. Experiments and Validation
|
|
5747
|
+
9. Results and Measurements
|
|
5748
|
+
10. Cost and Efficiency Analysis
|
|
5749
|
+
11. Observability and Operational Findings
|
|
5750
|
+
12. Findings, Learnings, Limitations and Risks
|
|
5751
|
+
13. Conclusion
|
|
5752
|
+
14. Recommendation and Next Steps
|
|
5753
|
+
15. Traceability and Sources
|
|
5754
|
+
`
|
|
5755
|
+
);
|
|
5596
5756
|
var SKILLS = [
|
|
5597
5757
|
ADR_WRITING,
|
|
5598
5758
|
WORK_ITEM_REFINEMENT,
|
|
@@ -5603,10 +5763,11 @@ var SKILLS = [
|
|
|
5603
5763
|
IMPLEMENTATION_PLANNING,
|
|
5604
5764
|
MODULE_CONTEXT_REFINEMENT,
|
|
5605
5765
|
EVIDENCE_VERIFICATION,
|
|
5606
|
-
LEGACY_RISK_ASSESSMENT
|
|
5766
|
+
LEGACY_RISK_ASSESSMENT,
|
|
5767
|
+
POC_REPORT_WRITING
|
|
5607
5768
|
];
|
|
5608
5769
|
var SKILL_GROUPS = {
|
|
5609
|
-
delivery: ["work-item-refinement", "implementation-planning", "learning-capture", "evidence-verification", "legacy-risk-assessment"],
|
|
5770
|
+
delivery: ["work-item-refinement", "implementation-planning", "learning-capture", "evidence-verification", "legacy-risk-assessment", "poc-report-writing"],
|
|
5610
5771
|
tech: ["adr-writing", "ownership-suggestion", "graph-metadata-review", "module-context-refinement"],
|
|
5611
5772
|
integration: ["capsule-writing"]
|
|
5612
5773
|
};
|
|
@@ -8140,7 +8301,7 @@ function canonicalSkills() {
|
|
|
8140
8301
|
}
|
|
8141
8302
|
function versionOf(content) {
|
|
8142
8303
|
try {
|
|
8143
|
-
const v =
|
|
8304
|
+
const v = matter9(content).data.version;
|
|
8144
8305
|
return v === void 0 || v === null ? null : String(v);
|
|
8145
8306
|
} catch {
|
|
8146
8307
|
return null;
|
|
@@ -8402,9 +8563,9 @@ function parseWorkItemSource(frontmatter) {
|
|
|
8402
8563
|
reason: "No source metadata found."
|
|
8403
8564
|
};
|
|
8404
8565
|
}
|
|
8405
|
-
function renderSourceCompact(
|
|
8406
|
-
const parts = [
|
|
8407
|
-
if (
|
|
8566
|
+
function renderSourceCompact(source3) {
|
|
8567
|
+
const parts = [source3.type];
|
|
8568
|
+
if (source3.id) parts.push(source3.id);
|
|
8408
8569
|
return parts.join(" \xB7 ");
|
|
8409
8570
|
}
|
|
8410
8571
|
function optStr2(v) {
|
|
@@ -8740,6 +8901,11 @@ var definePocHypothesis = {
|
|
|
8740
8901
|
return { status: poc.hypothesisDefined ? "done" : "current", evidence: poc.hypothesisDefined ? [POC_ARTIFACT_PATH] : void 0, reason: poc.hypothesisDefined ? void 0 : "A POC starts with one testable hypothesis." };
|
|
8741
8902
|
}
|
|
8742
8903
|
};
|
|
8904
|
+
var ensurePocArtifact = {
|
|
8905
|
+
id: "poc-artifact",
|
|
8906
|
+
label: "Create POC artifact",
|
|
8907
|
+
evaluate: (ctx) => exists(join(ctx.dir, POC_ARTIFACT_PATH)) ? { status: "done", evidence: [POC_ARTIFACT_PATH] } : { status: "current", command: "kaddo project mode poc", reason: "Create the canonical POC artifact before defining the experiment." }
|
|
8908
|
+
};
|
|
8743
8909
|
var definePocCriteria = {
|
|
8744
8910
|
id: "poc-success-criteria",
|
|
8745
8911
|
label: "Define success criteria",
|
|
@@ -8748,11 +8914,15 @@ var definePocCriteria = {
|
|
|
8748
8914
|
return { status: poc.successCriteriaCount > 0 ? "done" : "pending", evidence: poc.successCriteriaCount > 0 ? [POC_ARTIFACT_PATH] : void 0, reason: poc.successCriteriaCount > 0 ? void 0 : "Define observable evidence for the hypothesis." };
|
|
8749
8915
|
}
|
|
8750
8916
|
};
|
|
8751
|
-
var describePocTech = {
|
|
8917
|
+
var describePocTech = {
|
|
8918
|
+
id: "poc-technical-context",
|
|
8919
|
+
label: "Document minimal technical context",
|
|
8920
|
+
evaluate: (ctx) => ctx.hasScan ? describeArchitecture.evaluate(ctx) : { status: "optional", command: "kaddo scan", reason: "Add technical context only when the experiment needs to inspect existing code." }
|
|
8921
|
+
};
|
|
8752
8922
|
var identifyPocResources = {
|
|
8753
8923
|
id: "poc-resources",
|
|
8754
8924
|
label: "Identify relevant resources",
|
|
8755
|
-
evaluate: (ctx) => exists(join(ctx.dir, "knowledge/resources")) ? { status: "done", evidence: ["knowledge/resources/"] } : { status: "
|
|
8925
|
+
evaluate: (ctx) => exists(join(ctx.dir, "knowledge/resources")) ? { status: "done", evidence: ["knowledge/resources/"] } : { status: "optional", command: "kaddo resources list", reason: "Add Project Resources only when the experiment depends on external systems." }
|
|
8756
8926
|
};
|
|
8757
8927
|
var createPocExperiment = {
|
|
8758
8928
|
id: "poc-create-experiment",
|
|
@@ -8772,7 +8942,17 @@ var concludePoc = {
|
|
|
8772
8942
|
return conclusion === "pending" ? { status: "pending", reason: "Record validated, rejected, or inconclusive from the evidence." } : { status: "done", evidence: [POC_ARTIFACT_PATH], reason: `Conclusion: ${conclusion}.` };
|
|
8773
8943
|
}
|
|
8774
8944
|
};
|
|
8775
|
-
var
|
|
8945
|
+
var finalPocReport = {
|
|
8946
|
+
id: "poc-final-report",
|
|
8947
|
+
label: "Generate final report (optional)",
|
|
8948
|
+
evaluate: (ctx) => {
|
|
8949
|
+
const report = buildPocReportContext(ctx.dir);
|
|
8950
|
+
if (!report.eligible) return { status: "optional", reason: "Available after recording a POC conclusion." };
|
|
8951
|
+
if (report.status === "current") return { status: "done", evidence: [report.latestReport.path] };
|
|
8952
|
+
return { status: "optional", command: "kaddo poc report", reason: report.status === "stale" ? "Latest report is stale." : "A final report is optional." };
|
|
8953
|
+
}
|
|
8954
|
+
};
|
|
8955
|
+
var POC_STEPS = [enableKaddo, ensurePocArtifact, definePocHypothesis, definePocCriteria, describePocTech, identifyPocResources, createPocExperiment, capturePocEvidence, concludePoc, finalPocReport];
|
|
8776
8956
|
function stepsForState(state) {
|
|
8777
8957
|
switch (state) {
|
|
8778
8958
|
case "new":
|
|
@@ -8876,6 +9056,7 @@ function mapNextStepId(id) {
|
|
|
8876
9056
|
"poc-resources": "poc-resources",
|
|
8877
9057
|
"poc-create-experiment": "poc-create-experiment",
|
|
8878
9058
|
"poc-evaluate": "poc-evaluate",
|
|
9059
|
+
"poc-final-report": "poc-final-report",
|
|
8879
9060
|
"poc-complete": "poc-evaluate"
|
|
8880
9061
|
};
|
|
8881
9062
|
return MAP[id] ?? id;
|
|
@@ -8890,7 +9071,7 @@ function buildProjectRoute(dir) {
|
|
|
8890
9071
|
return { id: d3.id, label: d3.label, ...result };
|
|
8891
9072
|
});
|
|
8892
9073
|
for (const s of steps) {
|
|
8893
|
-
if (s.id === ctx.nextStepId && s.status !== "done") {
|
|
9074
|
+
if (s.id === ctx.nextStepId && s.status !== "done" && s.status !== "optional") {
|
|
8894
9075
|
s.status = "current";
|
|
8895
9076
|
}
|
|
8896
9077
|
}
|
|
@@ -8902,8 +9083,8 @@ function buildProjectRoute(dir) {
|
|
|
8902
9083
|
break;
|
|
8903
9084
|
}
|
|
8904
9085
|
}
|
|
8905
|
-
const completed = steps.filter((s) => s.status === "done").length;
|
|
8906
|
-
const total = steps.length;
|
|
9086
|
+
const completed = steps.filter((s) => s.status === "done" && s.status !== "optional").length;
|
|
9087
|
+
const total = steps.filter((s) => s.status !== "optional").length;
|
|
8907
9088
|
const progressPercent = total > 0 ? Math.round(completed / total * 100) : 0;
|
|
8908
9089
|
return {
|
|
8909
9090
|
type: state,
|
|
@@ -8960,7 +9141,7 @@ function renderRouteCompact(route) {
|
|
|
8960
9141
|
}
|
|
8961
9142
|
|
|
8962
9143
|
// ../cli/src/core/metadata-health.ts
|
|
8963
|
-
import
|
|
9144
|
+
import matter10 from "gray-matter";
|
|
8964
9145
|
var KNOWLEDGE_FILES2 = [
|
|
8965
9146
|
"knowledge/business/business.md",
|
|
8966
9147
|
"knowledge/product/product.md",
|
|
@@ -8980,7 +9161,7 @@ function analyzeMetadataHealth(dir) {
|
|
|
8980
9161
|
let data;
|
|
8981
9162
|
try {
|
|
8982
9163
|
const raw = readFile(p);
|
|
8983
|
-
data =
|
|
9164
|
+
data = matter10(raw).data;
|
|
8984
9165
|
} catch {
|
|
8985
9166
|
continue;
|
|
8986
9167
|
}
|
|
@@ -9012,7 +9193,7 @@ function analyzeMetadataHealth(dir) {
|
|
|
9012
9193
|
if (isFile(p) && entry.endsWith(".md")) {
|
|
9013
9194
|
try {
|
|
9014
9195
|
const raw = readFile(p);
|
|
9015
|
-
const fm =
|
|
9196
|
+
const fm = matter10(raw).data;
|
|
9016
9197
|
if (fm.status === "done") {
|
|
9017
9198
|
const rel = p.replace(dir + "/", "").replace(dir + "\\", "").replace(/\\/g, "/");
|
|
9018
9199
|
findings.push({
|
|
@@ -9967,7 +10148,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
9967
10148
|
}
|
|
9968
10149
|
}
|
|
9969
10150
|
try {
|
|
9970
|
-
const body =
|
|
10151
|
+
const body = matter11(readFile(wi.filePath)).content;
|
|
9971
10152
|
if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
|
|
9972
10153
|
else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
|
|
9973
10154
|
if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
|
|
@@ -10375,7 +10556,7 @@ function parseRatioNumerator(s) {
|
|
|
10375
10556
|
}
|
|
10376
10557
|
var SAVINGS_DISCLAIMER = "These are evidence-based estimates, not exact ROI. Adjust assumptions with real team data.";
|
|
10377
10558
|
function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
10378
|
-
const { assumptions: a2, source } = loadAssumptions(dir);
|
|
10559
|
+
const { assumptions: a2, source: source3 } = loadAssumptions(dir);
|
|
10379
10560
|
const impact = buildImpactReport(dir, { scope: opts.scope, scopeSource: opts.scopeSource }, now);
|
|
10380
10561
|
const completed = impact.traceability.completed_work_items;
|
|
10381
10562
|
const wiWithOwnership = parseRatioNumerator(impact.traceability.work_items_connected_to_code);
|
|
@@ -10415,7 +10596,7 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10415
10596
|
if (impact.score === null || impact.score < 60 || graphQuality === "empty") {
|
|
10416
10597
|
level = "Low";
|
|
10417
10598
|
reasons.push("Impact score is low or the knowledge graph is empty.");
|
|
10418
|
-
} else if (impact.score >= 85 && graphQuality === "good" &&
|
|
10599
|
+
} else if (impact.score >= 85 && graphQuality === "good" && source3 === "file" && guard.available && guard.resolved > 0) {
|
|
10419
10600
|
level = "High";
|
|
10420
10601
|
reasons.push("Strong evidence, custom assumptions and recorded drift resolution.");
|
|
10421
10602
|
} else {
|
|
@@ -10429,9 +10610,9 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10429
10610
|
} else {
|
|
10430
10611
|
reasons.push("Guard history available, but no resolved drift warnings have been recorded yet.");
|
|
10431
10612
|
}
|
|
10432
|
-
reasons.push(
|
|
10613
|
+
reasons.push(source3 === "file" ? "Assumptions come from `.kaddo/savings.yml`." : "Savings assumptions are defaults and should be calibrated.");
|
|
10433
10614
|
const actions = [];
|
|
10434
|
-
if (
|
|
10615
|
+
if (source3 === "default") actions.push("Run `kaddo savings init` to customize assumptions.");
|
|
10435
10616
|
actions.push("Calibrate hourly cost and team assumptions with real team data.");
|
|
10436
10617
|
actions.push("Run `kaddo impact` to verify the underlying evidence quality.");
|
|
10437
10618
|
actions.push("Use this report as directional evidence, not accounting data.");
|
|
@@ -10449,7 +10630,7 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10449
10630
|
scope: impact.scope,
|
|
10450
10631
|
currency: a2.currency,
|
|
10451
10632
|
disclaimer: SAVINGS_DISCLAIMER,
|
|
10452
|
-
assumptions_source:
|
|
10633
|
+
assumptions_source: source3,
|
|
10453
10634
|
assumptions: {
|
|
10454
10635
|
hourly_cost: a2.hourly_cost,
|
|
10455
10636
|
context_preparation_minutes_saved_per_work_item: a2.context_preparation_minutes_saved_per_work_item,
|
|
@@ -10680,7 +10861,7 @@ function serializeDriftJson(r2) {
|
|
|
10680
10861
|
}
|
|
10681
10862
|
|
|
10682
10863
|
// src/workitems.ts
|
|
10683
|
-
import
|
|
10864
|
+
import matter12 from "gray-matter";
|
|
10684
10865
|
var VALID_SOURCES2 = /* @__PURE__ */ new Set(["manual", "roadmap", "jira", "github", "notion", "xlsx", "csv", "api", "external", "unknown"]);
|
|
10685
10866
|
function optStr3(v) {
|
|
10686
10867
|
return typeof v === "string" && v.trim() ? v.trim() : void 0;
|
|
@@ -10771,7 +10952,7 @@ function listWorkItems(root) {
|
|
|
10771
10952
|
if (raw === null) continue;
|
|
10772
10953
|
let parsed;
|
|
10773
10954
|
try {
|
|
10774
|
-
parsed =
|
|
10955
|
+
parsed = matter12(raw);
|
|
10775
10956
|
} catch {
|
|
10776
10957
|
continue;
|
|
10777
10958
|
}
|
|
@@ -10780,8 +10961,8 @@ function listWorkItems(root) {
|
|
|
10780
10961
|
const status = lifecycleOf(data, rel);
|
|
10781
10962
|
const code = strArray2(data.code);
|
|
10782
10963
|
const domains = strArray2(data.domains);
|
|
10783
|
-
const
|
|
10784
|
-
const readiness = status === "draft" ? assessReadiness(data, parsed.content, code, domains,
|
|
10964
|
+
const source3 = parseSource(data);
|
|
10965
|
+
const readiness = status === "draft" ? assessReadiness(data, parsed.content, code, domains, source3) : void 0;
|
|
10785
10966
|
items.push({
|
|
10786
10967
|
id: String(data.id ?? "") || rel,
|
|
10787
10968
|
title: String(data.title ?? ""),
|
|
@@ -10794,7 +10975,7 @@ function listWorkItems(root) {
|
|
|
10794
10975
|
capabilities: strArray2(data.capabilities),
|
|
10795
10976
|
decisions: strArray2(data.decisions),
|
|
10796
10977
|
capsules: strArray2(data.capsules),
|
|
10797
|
-
source,
|
|
10978
|
+
source: source3,
|
|
10798
10979
|
domains,
|
|
10799
10980
|
readiness,
|
|
10800
10981
|
...data.implementation_status ? { implementation_status: String(data.implementation_status) } : {},
|
|
@@ -10811,7 +10992,7 @@ function listWorkItems(root) {
|
|
|
10811
10992
|
}
|
|
10812
10993
|
return items.sort((a2, b2) => a2.id.localeCompare(b2.id));
|
|
10813
10994
|
}
|
|
10814
|
-
function assessReadiness(data, content, code, domains,
|
|
10995
|
+
function assessReadiness(data, content, code, domains, source3) {
|
|
10815
10996
|
const body = content.trim();
|
|
10816
10997
|
const lower = body.toLowerCase();
|
|
10817
10998
|
const warnings = [];
|
|
@@ -10965,9 +11146,28 @@ var RESOURCES = [
|
|
|
10965
11146
|
root,
|
|
10966
11147
|
"kaddo://poc",
|
|
10967
11148
|
"knowledge/delivery/poc.md",
|
|
10968
|
-
"POC artifact not found.
|
|
11149
|
+
"POC artifact not found. Initialize in POC mode or switch with `kaddo project mode poc`."
|
|
10969
11150
|
)
|
|
10970
11151
|
},
|
|
11152
|
+
{
|
|
11153
|
+
uri: "kaddo://poc-report",
|
|
11154
|
+
name: "Kaddo final POC report",
|
|
11155
|
+
description: "Latest final POC report, or deterministic report status and source selection.",
|
|
11156
|
+
mimeType: "text/markdown",
|
|
11157
|
+
read: (root) => {
|
|
11158
|
+
const context = buildPocReportContext(root);
|
|
11159
|
+
if (context.latestReport) {
|
|
11160
|
+
const saved = readText(root, context.latestReport.path);
|
|
11161
|
+
if (saved) return text2("kaddo://poc-report", saved);
|
|
11162
|
+
}
|
|
11163
|
+
const lines = [
|
|
11164
|
+
`Final POC report: ${context.status}`,
|
|
11165
|
+
`Conclusion: ${context.conclusion}`,
|
|
11166
|
+
context.handoff
|
|
11167
|
+
];
|
|
11168
|
+
return text2("kaddo://poc-report", lines.join("\n\n"));
|
|
11169
|
+
}
|
|
11170
|
+
},
|
|
10971
11171
|
{
|
|
10972
11172
|
uri: "kaddo://capsules",
|
|
10973
11173
|
name: "Kaddo knowledge capsules",
|
|
@@ -11251,14 +11451,14 @@ function getPrompt(root, name) {
|
|
|
11251
11451
|
}
|
|
11252
11452
|
|
|
11253
11453
|
// src/tools.ts
|
|
11254
|
-
import
|
|
11454
|
+
import matter16 from "gray-matter";
|
|
11255
11455
|
|
|
11256
11456
|
// ../cli/dist/core.js
|
|
11257
11457
|
import fs3 from "fs";
|
|
11258
11458
|
import path3 from "path";
|
|
11259
11459
|
import { z as z3 } from "zod";
|
|
11260
11460
|
import { parse as parseYaml10, stringify as stringifyYaml5 } from "yaml";
|
|
11261
|
-
import
|
|
11461
|
+
import matter13 from "gray-matter";
|
|
11262
11462
|
import matter22 from "gray-matter";
|
|
11263
11463
|
import { parse as parseYaml62, stringify as stringifyYaml42 } from "yaml";
|
|
11264
11464
|
import { parse as parseYaml22 } from "yaml";
|
|
@@ -11269,25 +11469,27 @@ import matter32 from "gray-matter";
|
|
|
11269
11469
|
import matter42 from "gray-matter";
|
|
11270
11470
|
import matter52 from "gray-matter";
|
|
11271
11471
|
import { parse as parseYaml72 } from "yaml";
|
|
11472
|
+
import { createHash as createHash2 } from "crypto";
|
|
11272
11473
|
import matter62 from "gray-matter";
|
|
11474
|
+
import matter72 from "gray-matter";
|
|
11273
11475
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11274
11476
|
import { dirname as dirname2, join as join22, parse as parse2 } from "path";
|
|
11275
11477
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
11276
|
-
import matter72 from "gray-matter";
|
|
11277
11478
|
import matter82 from "gray-matter";
|
|
11278
11479
|
import matter92 from "gray-matter";
|
|
11480
|
+
import matter102 from "gray-matter";
|
|
11279
11481
|
import { parse as parseYaml82, stringify as stringifyYaml52 } from "yaml";
|
|
11280
11482
|
import fs4 from "fs";
|
|
11281
11483
|
import path4 from "path";
|
|
11282
11484
|
import crypto2 from "crypto";
|
|
11283
|
-
import
|
|
11485
|
+
import matter112 from "gray-matter";
|
|
11284
11486
|
import crypto3 from "crypto";
|
|
11285
11487
|
import fs5 from "fs";
|
|
11286
|
-
import matter112 from "gray-matter";
|
|
11287
11488
|
import matter122 from "gray-matter";
|
|
11489
|
+
import matter132 from "gray-matter";
|
|
11288
11490
|
import { parse as parseYaml92, stringify as stringifyYaml6 } from "yaml";
|
|
11289
11491
|
import { parse as parseYaml102, stringify as stringifyYaml7 } from "yaml";
|
|
11290
|
-
import
|
|
11492
|
+
import matter14 from "gray-matter";
|
|
11291
11493
|
function exists2(p) {
|
|
11292
11494
|
return fs3.existsSync(p);
|
|
11293
11495
|
}
|
|
@@ -11463,7 +11665,7 @@ function parseImpactAnalysis2(data) {
|
|
|
11463
11665
|
}
|
|
11464
11666
|
function parseArtifact2(filePath, raw) {
|
|
11465
11667
|
try {
|
|
11466
|
-
const { data } =
|
|
11668
|
+
const { data } = matter13(raw);
|
|
11467
11669
|
const globs = Array.isArray(data.code) ? data.code.filter(Boolean) : [];
|
|
11468
11670
|
return {
|
|
11469
11671
|
filePath,
|
|
@@ -11577,12 +11779,12 @@ function classifyLayer2(posixPath) {
|
|
|
11577
11779
|
return "unknown";
|
|
11578
11780
|
}
|
|
11579
11781
|
function enrich2(dir, a2) {
|
|
11580
|
-
const
|
|
11782
|
+
const posix22 = toPosix7(a2.filePath);
|
|
11581
11783
|
const isWi = isWorkItemArtifact2(a2);
|
|
11582
11784
|
return {
|
|
11583
11785
|
...a2,
|
|
11584
11786
|
relPath: relativeTo2(dir, a2.filePath),
|
|
11585
|
-
layer: classifyLayer2(
|
|
11787
|
+
layer: classifyLayer2(posix22),
|
|
11586
11788
|
isWorkItem: isWi,
|
|
11587
11789
|
lifecycle: isWi ? lifecycleStateOf2({ status: a2.status, filePath: a2.filePath }) : void 0
|
|
11588
11790
|
};
|
|
@@ -13612,6 +13814,177 @@ function buildTechDecisions2(dir) {
|
|
|
13612
13814
|
function hasUnmaterializedDecisions(td) {
|
|
13613
13815
|
return td.candidates > 0 && td.adrs === 0;
|
|
13614
13816
|
}
|
|
13817
|
+
var POC_ARTIFACT_PATH2 = "knowledge/delivery/poc.md";
|
|
13818
|
+
var PLACEHOLDER2 = /^[_<].*[_>]$|^(describe|list|document|what|how)\b/i;
|
|
13819
|
+
function section3(markdown, heading) {
|
|
13820
|
+
const match = markdown.match(new RegExp(`^##\\s+${heading}\\s*$([\\s\\S]*?)(?=^##\\s+|(?![\\s\\S]))`, "im"));
|
|
13821
|
+
return match?.[1] ?? "";
|
|
13822
|
+
}
|
|
13823
|
+
function meaningful2(text22) {
|
|
13824
|
+
return text22.split(/\r?\n/).some((line) => {
|
|
13825
|
+
const value = line.trim().replace(/^[-*]\s+(?:\[[^\]]+\]\s*)?/, "");
|
|
13826
|
+
return Boolean(value) && !PLACEHOLDER2.test(value) && !/^status:\s*(pending|tbd)$/i.test(value);
|
|
13827
|
+
});
|
|
13828
|
+
}
|
|
13829
|
+
function readPocSummary2(dir) {
|
|
13830
|
+
const path52 = join3(dir, POC_ARTIFACT_PATH2);
|
|
13831
|
+
if (!exists2(path52)) {
|
|
13832
|
+
return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
|
|
13833
|
+
}
|
|
13834
|
+
let markdown = "";
|
|
13835
|
+
try {
|
|
13836
|
+
markdown = readFile2(path52);
|
|
13837
|
+
} catch {
|
|
13838
|
+
return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
|
|
13839
|
+
}
|
|
13840
|
+
const criteria = section3(markdown, "Success Criteria").split(/\r?\n/).filter((line) => /^\s*[-*]\s+(?:\[[ xX]\]\s+)?/.test(line)).filter((line) => meaningful2(line)).length;
|
|
13841
|
+
const conclusionBody = section3(markdown, "Conclusion");
|
|
13842
|
+
const conclusionMatch = conclusionBody.match(/\b(?:status|outcome)\s*:\s*(validated|rejected|inconclusive)\b/i);
|
|
13843
|
+
return {
|
|
13844
|
+
exists: true,
|
|
13845
|
+
hypothesisDefined: meaningful2(section3(markdown, "Hypothesis")),
|
|
13846
|
+
successCriteriaCount: criteria,
|
|
13847
|
+
constraintsDefined: meaningful2(section3(markdown, "Constraints")),
|
|
13848
|
+
nonGoalsDefined: meaningful2(section3(markdown, "Non-goals")),
|
|
13849
|
+
conclusion: conclusionMatch ? conclusionMatch[1].toLowerCase() : "pending"
|
|
13850
|
+
};
|
|
13851
|
+
}
|
|
13852
|
+
var POC_REPORT_PREFIX2 = "poc-report-v";
|
|
13853
|
+
var SECRET_LINE2 = /(^|\s)(?:[A-Z][A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE_KEY)|(?:token|secret|password|api[_-]?key))\s*[:=]\s*\S+/i;
|
|
13854
|
+
var reportName2 = /^poc-report-v(\d{3,})\.md$/;
|
|
13855
|
+
function sha2(value) {
|
|
13856
|
+
return createHash2("sha256").update(value).digest("hex");
|
|
13857
|
+
}
|
|
13858
|
+
function posix2(value) {
|
|
13859
|
+
return value.replace(/\\/g, "/");
|
|
13860
|
+
}
|
|
13861
|
+
function redactSecrets2(value) {
|
|
13862
|
+
return value.split(/\r?\n/).map((line) => {
|
|
13863
|
+
if (!SECRET_LINE2.test(line)) return line;
|
|
13864
|
+
return line.replace(/([:=]\s*)\S+.*/, "$1[redacted]");
|
|
13865
|
+
}).join("\n");
|
|
13866
|
+
}
|
|
13867
|
+
function source2(dir, path52, kind) {
|
|
13868
|
+
const absolute = join3(dir, path52);
|
|
13869
|
+
if (!exists2(absolute)) return null;
|
|
13870
|
+
const raw = readFile2(absolute);
|
|
13871
|
+
return { path: posix2(path52), kind, digest: sha2(raw), content: redactSecrets2(raw) };
|
|
13872
|
+
}
|
|
13873
|
+
function reportFiles2(dir) {
|
|
13874
|
+
const delivery = join3(dir, "knowledge", "delivery");
|
|
13875
|
+
return readDir2(delivery).flatMap((name) => {
|
|
13876
|
+
const match = name.match(reportName2);
|
|
13877
|
+
if (!match) return [];
|
|
13878
|
+
try {
|
|
13879
|
+
const parsed = matter62(readFile2(join3(delivery, name)));
|
|
13880
|
+
const fingerprint = typeof parsed.data.source_fingerprint === "string" ? parsed.data.source_fingerprint : "";
|
|
13881
|
+
const manifest = parsed.data.source_manifest;
|
|
13882
|
+
const sourceManifest = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? Object.fromEntries(Object.entries(manifest).filter(([, digest]) => typeof digest === "string")) : {};
|
|
13883
|
+
return [{ path: `knowledge/delivery/${name}`, version: Number(match[1]), sourceFingerprint: fingerprint, sourceManifest }];
|
|
13884
|
+
} catch {
|
|
13885
|
+
return [];
|
|
13886
|
+
}
|
|
13887
|
+
}).sort((a2, b2) => b2.version - a2.version);
|
|
13888
|
+
}
|
|
13889
|
+
function reportPath2(version) {
|
|
13890
|
+
return `knowledge/delivery/${POC_REPORT_PREFIX2}${String(version).padStart(3, "0")}.md`;
|
|
13891
|
+
}
|
|
13892
|
+
function buildPocReportContext2(dir) {
|
|
13893
|
+
const poc = readPocSummary2(dir);
|
|
13894
|
+
const reports = reportFiles2(dir);
|
|
13895
|
+
const latestReport = reports[0] ?? null;
|
|
13896
|
+
const nextPath = reportPath2((latestReport?.version ?? 0) + 1);
|
|
13897
|
+
if (!poc.exists || poc.conclusion === "pending") {
|
|
13898
|
+
return {
|
|
13899
|
+
eligible: false,
|
|
13900
|
+
conclusion: poc.conclusion,
|
|
13901
|
+
status: latestReport ? "stale" : "missing",
|
|
13902
|
+
latestReport,
|
|
13903
|
+
nextPath,
|
|
13904
|
+
sourceFingerprint: "",
|
|
13905
|
+
sources: [],
|
|
13906
|
+
changesSinceLatest: [],
|
|
13907
|
+
handoff: "POC conclusion is still pending. Record validated, rejected, or inconclusive before generating a final report."
|
|
13908
|
+
};
|
|
13909
|
+
}
|
|
13910
|
+
const sources = [];
|
|
13911
|
+
const add = (path52, kind) => {
|
|
13912
|
+
if (sources.some((item2) => item2.path === posix2(path52))) return;
|
|
13913
|
+
const item = source2(dir, path52, kind);
|
|
13914
|
+
if (item) sources.push(item);
|
|
13915
|
+
};
|
|
13916
|
+
add(POC_ARTIFACT_PATH2, "poc");
|
|
13917
|
+
const pocText = readFile2(join3(dir, POC_ARTIFACT_PATH2));
|
|
13918
|
+
const knowledge = discoverKnowledge2(dir);
|
|
13919
|
+
const workItems = discoverWorkItems2(dir);
|
|
13920
|
+
const referencedWorkItems = workItems.filter((wi) => wi.lifecycle === "completed" && wi.id && new RegExp(`\\b${wi.id}\\b`).test(pocText));
|
|
13921
|
+
const resourceIds = /* @__PURE__ */ new Set();
|
|
13922
|
+
const decisionIds = /* @__PURE__ */ new Set();
|
|
13923
|
+
for (const wi of referencedWorkItems) {
|
|
13924
|
+
add(wi.relPath, "work-item");
|
|
13925
|
+
for (const resource of parseWorkItemResources2(wi.rawFrontmatter)) resourceIds.add(resource.id);
|
|
13926
|
+
const decisions = wi.rawFrontmatter.decisions;
|
|
13927
|
+
if (Array.isArray(decisions)) decisions.forEach((id) => decisionIds.add(String(id)));
|
|
13928
|
+
}
|
|
13929
|
+
for (const id of resourceIds) {
|
|
13930
|
+
const resource = getResource2(dir, id);
|
|
13931
|
+
if (resource) add(resource.path, "resource");
|
|
13932
|
+
}
|
|
13933
|
+
for (const artifact of knowledge) {
|
|
13934
|
+
if (artifact.isWorkItem || !artifact.id) continue;
|
|
13935
|
+
if (decisionIds.has(artifact.id) || referencedWorkItems.some((wi) => new RegExp(`\\b${artifact.id}\\b`).test(readFile2(wi.filePath)))) add(artifact.relPath, "knowledge");
|
|
13936
|
+
}
|
|
13937
|
+
sources.sort((a2, b2) => a2.path.localeCompare(b2.path));
|
|
13938
|
+
const sourceFingerprint = sha2(sources.map((item) => `${item.path}\0${item.digest}`).join("\n"));
|
|
13939
|
+
const status = !latestReport ? "missing" : latestReport.sourceFingerprint === sourceFingerprint ? "current" : "stale";
|
|
13940
|
+
const currentManifest = Object.fromEntries(sources.map((item) => [item.path, item.digest]));
|
|
13941
|
+
const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path52) => currentManifest[path52] !== latestReport?.sourceManifest[path52]).sort() : [];
|
|
13942
|
+
const sourceList = sources.map((item) => `- ${item.path} (${item.kind})`).join("\n") || "- No sources selected.";
|
|
13943
|
+
return {
|
|
13944
|
+
eligible: true,
|
|
13945
|
+
conclusion: poc.conclusion,
|
|
13946
|
+
status,
|
|
13947
|
+
latestReport,
|
|
13948
|
+
nextPath,
|
|
13949
|
+
sourceFingerprint,
|
|
13950
|
+
sources,
|
|
13951
|
+
changesSinceLatest,
|
|
13952
|
+
handoff: [
|
|
13953
|
+
"Create a POC Final Report from the supplied sources only. Do not explore the repository.",
|
|
13954
|
+
`Canonical conclusion: ${poc.conclusion}. Do not contradict it.`,
|
|
13955
|
+
"Use the 15 canonical sections. Mark missing evidence as Not evaluated, Not applicable, or No evidence available.",
|
|
13956
|
+
"Distinguish measured, calculated, estimated, and projected values. Never include secret values.",
|
|
13957
|
+
"",
|
|
13958
|
+
"Selected sources:",
|
|
13959
|
+
sourceList
|
|
13960
|
+
].join("\n")
|
|
13961
|
+
};
|
|
13962
|
+
}
|
|
13963
|
+
function canonicalReport(context, proposal) {
|
|
13964
|
+
const supersedes = context.latestReport?.path.split("/").pop();
|
|
13965
|
+
const data = {
|
|
13966
|
+
type: "poc-report",
|
|
13967
|
+
report_version: Number(context.nextPath.match(/v(\d+)\.md$/)?.[1] ?? 1),
|
|
13968
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
13969
|
+
poc_conclusion: context.conclusion,
|
|
13970
|
+
source_fingerprint: context.sourceFingerprint,
|
|
13971
|
+
sources: context.sources.map((item) => item.path),
|
|
13972
|
+
source_manifest: Object.fromEntries(context.sources.map((item) => [item.path, item.digest]))
|
|
13973
|
+
};
|
|
13974
|
+
if (supersedes) data.supersedes = supersedes;
|
|
13975
|
+
return matter62.stringify(redactSecrets2(proposal).trimEnd() + "\n", data);
|
|
13976
|
+
}
|
|
13977
|
+
function persistPocReport(dir, proposal, opts = {}) {
|
|
13978
|
+
const context = buildPocReportContext2(dir);
|
|
13979
|
+
if (!context.eligible) throw new Error(context.handoff);
|
|
13980
|
+
if (context.status === "current" && !opts.force) return { status: "current", path: context.latestReport.path, message: "The current POC report already represents the latest evidence." };
|
|
13981
|
+
if (!opts.confirm) return { status: "needs_confirmation", path: context.nextPath, content: canonicalReport(context, proposal), message: "Review the proposed report and confirm persistence." };
|
|
13982
|
+
const output = join3(dir, context.nextPath);
|
|
13983
|
+
if (exists2(output)) throw new Error(`POC report already exists: ${context.nextPath}`);
|
|
13984
|
+
const content = canonicalReport(context, proposal);
|
|
13985
|
+
writeFile2(output, content);
|
|
13986
|
+
return { status: "persisted", path: context.nextPath, content };
|
|
13987
|
+
}
|
|
13615
13988
|
function resolveVersion2() {
|
|
13616
13989
|
try {
|
|
13617
13990
|
let dir = dirname2(fileURLToPath2(import.meta.url));
|
|
@@ -14280,6 +14653,50 @@ their intersection with the affected areas, and recommended actions.
|
|
|
14280
14653
|
- The assessment is proportional \u2014 a small change gets a brief assessment.
|
|
14281
14654
|
`
|
|
14282
14655
|
);
|
|
14656
|
+
var POC_REPORT_WRITING2 = skill2(
|
|
14657
|
+
"poc-report-writing",
|
|
14658
|
+
"POC Final Report Writing Skill",
|
|
14659
|
+
"delivery",
|
|
14660
|
+
["implementation-agent", "work-item-agent"],
|
|
14661
|
+
`
|
|
14662
|
+
# POC Final Report Writing Skill
|
|
14663
|
+
|
|
14664
|
+
## Purpose
|
|
14665
|
+
|
|
14666
|
+
Turn a supplied deterministic POC report context into a concise, traceable final report.
|
|
14667
|
+
|
|
14668
|
+
## When to use
|
|
14669
|
+
|
|
14670
|
+
After a POC has a canonical conclusion and Core has prepared its deterministic report context.
|
|
14671
|
+
|
|
14672
|
+
## Rules
|
|
14673
|
+
|
|
14674
|
+
- Use only the supplied context; do not scan the repository or infer missing results.
|
|
14675
|
+
- Treat \`poc.md\` as canonical for the conclusion. Never contradict it.
|
|
14676
|
+
- Keep facts, measurements, calculations, estimates, and recommendations distinct.
|
|
14677
|
+
- Never include credential values, tokens, private keys, passwords, or connection strings.
|
|
14678
|
+
- Use \`Not evaluated in this POC.\`, \`Not applicable.\`, or \`No evidence available.\` when sources do not support a section.
|
|
14679
|
+
- Return a proposal for human review; Core persists it only after confirmation.
|
|
14680
|
+
|
|
14681
|
+
## Required structure
|
|
14682
|
+
|
|
14683
|
+
1. Executive Summary
|
|
14684
|
+
2. Problem and Objective
|
|
14685
|
+
3. Hypothesis and Success Criteria
|
|
14686
|
+
4. Scope, Constraints and Non-goals
|
|
14687
|
+
5. Technical Approach and Architecture
|
|
14688
|
+
6. Infrastructure and Project Resources
|
|
14689
|
+
7. Implementation and Relevant Technical Decisions
|
|
14690
|
+
8. Experiments and Validation
|
|
14691
|
+
9. Results and Measurements
|
|
14692
|
+
10. Cost and Efficiency Analysis
|
|
14693
|
+
11. Observability and Operational Findings
|
|
14694
|
+
12. Findings, Learnings, Limitations and Risks
|
|
14695
|
+
13. Conclusion
|
|
14696
|
+
14. Recommendation and Next Steps
|
|
14697
|
+
15. Traceability and Sources
|
|
14698
|
+
`
|
|
14699
|
+
);
|
|
14283
14700
|
var SKILLS2 = [
|
|
14284
14701
|
ADR_WRITING2,
|
|
14285
14702
|
WORK_ITEM_REFINEMENT2,
|
|
@@ -14290,10 +14707,11 @@ var SKILLS2 = [
|
|
|
14290
14707
|
IMPLEMENTATION_PLANNING2,
|
|
14291
14708
|
MODULE_CONTEXT_REFINEMENT2,
|
|
14292
14709
|
EVIDENCE_VERIFICATION2,
|
|
14293
|
-
LEGACY_RISK_ASSESSMENT2
|
|
14710
|
+
LEGACY_RISK_ASSESSMENT2,
|
|
14711
|
+
POC_REPORT_WRITING2
|
|
14294
14712
|
];
|
|
14295
14713
|
var SKILL_GROUPS2 = {
|
|
14296
|
-
delivery: ["work-item-refinement", "implementation-planning", "learning-capture", "evidence-verification", "legacy-risk-assessment"],
|
|
14714
|
+
delivery: ["work-item-refinement", "implementation-planning", "learning-capture", "evidence-verification", "legacy-risk-assessment", "poc-report-writing"],
|
|
14297
14715
|
tech: ["adr-writing", "ownership-suggestion", "graph-metadata-review", "module-context-refinement"],
|
|
14298
14716
|
integration: ["capsule-writing"]
|
|
14299
14717
|
};
|
|
@@ -17138,7 +17556,6 @@ var LEGACY_STEPS2 = [
|
|
|
17138
17556
|
runGuard2,
|
|
17139
17557
|
captureLearning2
|
|
17140
17558
|
];
|
|
17141
|
-
var describePocTech2 = { ...describeArchitecture2, id: "poc-technical-context", label: "Document minimal technical context" };
|
|
17142
17559
|
var USER_FACING_KEYWORDS_EN = [
|
|
17143
17560
|
"user",
|
|
17144
17561
|
"visitor",
|
|
@@ -17542,7 +17959,7 @@ function parseOriginalSnapshot(fm) {
|
|
|
17542
17959
|
function readBody(filePath) {
|
|
17543
17960
|
try {
|
|
17544
17961
|
const raw = readFile2(filePath);
|
|
17545
|
-
return
|
|
17962
|
+
return matter102(raw).content;
|
|
17546
17963
|
} catch {
|
|
17547
17964
|
return "";
|
|
17548
17965
|
}
|
|
@@ -17827,7 +18244,7 @@ function ensureH1(body, title) {
|
|
|
17827
18244
|
${trimmed}`;
|
|
17828
18245
|
}
|
|
17829
18246
|
function serialize2(data, body) {
|
|
17830
|
-
return
|
|
18247
|
+
return matter112.stringify(`
|
|
17831
18248
|
${body.trim()}
|
|
17832
18249
|
`, data);
|
|
17833
18250
|
}
|
|
@@ -17862,7 +18279,7 @@ function createWorkItem(dir, opts) {
|
|
|
17862
18279
|
const title = intent.split(/\r?\n/)[0].trim().slice(0, 120);
|
|
17863
18280
|
const today22 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
17864
18281
|
const summary = opts.summary?.trim() || intent;
|
|
17865
|
-
const
|
|
18282
|
+
const source22 = opts.source ? { ...opts.source, inferred: false } : { type: "manual", inferred: false };
|
|
17866
18283
|
const data = {
|
|
17867
18284
|
type,
|
|
17868
18285
|
id,
|
|
@@ -17870,7 +18287,7 @@ function createWorkItem(dir, opts) {
|
|
|
17870
18287
|
status: "draft",
|
|
17871
18288
|
work_type: type,
|
|
17872
18289
|
created_at: today22,
|
|
17873
|
-
source,
|
|
18290
|
+
source: source22,
|
|
17874
18291
|
generated_by: "kaddo-admin",
|
|
17875
18292
|
affected_modules: [],
|
|
17876
18293
|
summary
|
|
@@ -18618,7 +19035,7 @@ function detectFormat(raw) {
|
|
|
18618
19035
|
if (!trimmed) throw new ImportError("EMPTY_INPUT", "Import content is empty.");
|
|
18619
19036
|
if (trimmed.startsWith("---")) {
|
|
18620
19037
|
try {
|
|
18621
|
-
const { data } =
|
|
19038
|
+
const { data } = matter122(trimmed);
|
|
18622
19039
|
if (data && typeof data === "object" && Object.keys(data).length > 0) {
|
|
18623
19040
|
const d3 = data;
|
|
18624
19041
|
if (d3.type === "work-item" || d3.id && typeof d3.id === "string" && /^WI-\d+/.test(d3.id)) {
|
|
@@ -18640,7 +19057,7 @@ function parseContent(raw) {
|
|
|
18640
19057
|
let bodyText = trimmed;
|
|
18641
19058
|
if (format === "kaddo-frontmatter" || format === "markdown-frontmatter") {
|
|
18642
19059
|
try {
|
|
18643
|
-
const parsed =
|
|
19060
|
+
const parsed = matter122(trimmed);
|
|
18644
19061
|
frontmatterFields = parsed.data ?? {};
|
|
18645
19062
|
bodyText = parsed.content.trim();
|
|
18646
19063
|
} catch {
|
|
@@ -18712,12 +19129,12 @@ function normalizeImportFields(parsed, opts) {
|
|
|
18712
19129
|
const intent = parsed.title;
|
|
18713
19130
|
const body = hasRealBody ? parsed.body : void 0;
|
|
18714
19131
|
const summary = hasRealBody ? deriveShortSummary(parsed) : parsed.title;
|
|
18715
|
-
const
|
|
19132
|
+
const source22 = {
|
|
18716
19133
|
type: opts.importSource === "chat" ? "chat" : "external",
|
|
18717
19134
|
imported_at: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
18718
19135
|
};
|
|
18719
|
-
|
|
18720
|
-
|
|
19136
|
+
source22.source_format = opts.sourceFormat;
|
|
19137
|
+
source22.source_hash = opts.sourceHash;
|
|
18721
19138
|
const snapshot = { title: parsed.title };
|
|
18722
19139
|
if (summary !== parsed.title) snapshot.description = summary;
|
|
18723
19140
|
if (typeof parsed.candidateFields.type === "string") snapshot.type = parsed.candidateFields.type;
|
|
@@ -18726,7 +19143,7 @@ function normalizeImportFields(parsed, opts) {
|
|
|
18726
19143
|
const labels = parsed.candidateFields.labels.filter((l2) => typeof l2 === "string");
|
|
18727
19144
|
if (labels.length > 0) snapshot.labels = labels;
|
|
18728
19145
|
}
|
|
18729
|
-
return { type, intent, summary, body, source, snapshot, discardedFields };
|
|
19146
|
+
return { type, intent, summary, body, source: source22, snapshot, discardedFields };
|
|
18730
19147
|
}
|
|
18731
19148
|
function computeContentHash(raw) {
|
|
18732
19149
|
return crypto3.createHash("sha256").update(raw.trim(), "utf-8").digest("hex");
|
|
@@ -18737,10 +19154,10 @@ function findDuplicateByHash(dir, hash) {
|
|
|
18737
19154
|
const raw = readFile2(item.filePath);
|
|
18738
19155
|
if (!raw) continue;
|
|
18739
19156
|
try {
|
|
18740
|
-
const { data } =
|
|
18741
|
-
const
|
|
18742
|
-
if (
|
|
18743
|
-
const s =
|
|
19157
|
+
const { data } = matter122(raw);
|
|
19158
|
+
const source22 = data?.source;
|
|
19159
|
+
if (source22 && typeof source22 === "object" && !Array.isArray(source22)) {
|
|
19160
|
+
const s = source22;
|
|
18744
19161
|
if (s.source_hash === hash) {
|
|
18745
19162
|
return { workItemId: data.id ?? item.id ?? item.title, path: item.relPath };
|
|
18746
19163
|
}
|
|
@@ -19632,7 +20049,7 @@ function buildInitiativeContent(id, input) {
|
|
|
19632
20049
|
if (input.priority) data.priority = input.priority;
|
|
19633
20050
|
if (input.source) data.source = input.source;
|
|
19634
20051
|
if (input.sourceId) data.source_id = input.sourceId;
|
|
19635
|
-
return
|
|
20052
|
+
return matter14.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
|
|
19636
20053
|
}
|
|
19637
20054
|
function createInitiative(dir, input) {
|
|
19638
20055
|
if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
|
|
@@ -19656,10 +20073,10 @@ function transitionInitiative(dir, id, to) {
|
|
|
19656
20073
|
throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
|
|
19657
20074
|
}
|
|
19658
20075
|
const raw = readFile2(ini.filePath);
|
|
19659
|
-
const { data, content } =
|
|
20076
|
+
const { data, content } = matter14(raw, {});
|
|
19660
20077
|
data.status = to;
|
|
19661
20078
|
if (to === "completed") data.completed_at = today2();
|
|
19662
|
-
const newContent =
|
|
20079
|
+
const newContent = matter14.stringify(content, data);
|
|
19663
20080
|
writeFile2(ini.filePath, newContent);
|
|
19664
20081
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19665
20082
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
|
|
@@ -19669,7 +20086,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
19669
20086
|
const ini = getInitiative(dir, id);
|
|
19670
20087
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
19671
20088
|
const raw = readFile2(ini.filePath);
|
|
19672
|
-
const { data, content } =
|
|
20089
|
+
const { data, content } = matter14(raw, {});
|
|
19673
20090
|
const existing = Array.isArray(data.candidates) ? data.candidates : [];
|
|
19674
20091
|
const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
|
|
19675
20092
|
const normalized = normalizeCandidate(candidate, fallbackId);
|
|
@@ -19677,7 +20094,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
19677
20094
|
throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
|
|
19678
20095
|
}
|
|
19679
20096
|
data.candidates = [...existing, candidateToData(normalized)];
|
|
19680
|
-
const newContent =
|
|
20097
|
+
const newContent = matter14.stringify(content, data);
|
|
19681
20098
|
writeFile2(ini.filePath, newContent);
|
|
19682
20099
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19683
20100
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
|
|
@@ -19687,7 +20104,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
19687
20104
|
const ini = getInitiative(dir, id);
|
|
19688
20105
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
19689
20106
|
const raw = readFile2(ini.filePath);
|
|
19690
|
-
const { data, content } =
|
|
20107
|
+
const { data, content } = matter14(raw, {});
|
|
19691
20108
|
if (patch.title !== void 0) {
|
|
19692
20109
|
if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
|
|
19693
20110
|
data.title = patch.title.trim();
|
|
@@ -19697,7 +20114,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
19697
20114
|
if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
|
|
19698
20115
|
if (patch.domains !== void 0) data.domains = patch.domains;
|
|
19699
20116
|
if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
|
|
19700
|
-
const newContent =
|
|
20117
|
+
const newContent = matter14.stringify(content, data);
|
|
19701
20118
|
writeFile2(ini.filePath, newContent);
|
|
19702
20119
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19703
20120
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
|
|
@@ -19715,10 +20132,10 @@ function addExternalLink(dir, id, link) {
|
|
|
19715
20132
|
throw new InitiativeWriteError(`External link ${link.integration}:${link.externalId} already exists on ${id}.`);
|
|
19716
20133
|
}
|
|
19717
20134
|
const raw = readFile2(ini.filePath);
|
|
19718
|
-
const { data, content } =
|
|
20135
|
+
const { data, content } = matter14(raw, {});
|
|
19719
20136
|
const existing = Array.isArray(data.external_links) ? data.external_links : [];
|
|
19720
20137
|
data.external_links = [...existing, externalLinkToData(link)];
|
|
19721
|
-
const newContent =
|
|
20138
|
+
const newContent = matter14.stringify(content, data);
|
|
19722
20139
|
writeFile2(ini.filePath, newContent);
|
|
19723
20140
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19724
20141
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding an external link.");
|
|
@@ -19787,7 +20204,7 @@ function buildWorkItemContent(wiId, iniId, candidate) {
|
|
|
19787
20204
|
"_What did we learn? Update after completion._",
|
|
19788
20205
|
""
|
|
19789
20206
|
].join("\n");
|
|
19790
|
-
return
|
|
20207
|
+
return matter14.stringify("\n" + body, data);
|
|
19791
20208
|
}
|
|
19792
20209
|
function materializeCandidate(dir, id, candidateId) {
|
|
19793
20210
|
const ini = getInitiative(dir, id);
|
|
@@ -19804,13 +20221,13 @@ function materializeCandidate(dir, id, candidateId) {
|
|
|
19804
20221
|
ensureDir2(join3(dir, DRAFT_DIR));
|
|
19805
20222
|
writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
|
|
19806
20223
|
const raw = readFile2(ini.filePath);
|
|
19807
|
-
const { data, content } =
|
|
20224
|
+
const { data, content } = matter14(raw, {});
|
|
19808
20225
|
const cands = Array.isArray(data.candidates) ? data.candidates : [];
|
|
19809
20226
|
for (const c of cands) {
|
|
19810
20227
|
if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
|
|
19811
20228
|
}
|
|
19812
20229
|
data.candidates = cands;
|
|
19813
|
-
const newContent =
|
|
20230
|
+
const newContent = matter14.stringify(content, data);
|
|
19814
20231
|
writeFile2(ini.filePath, newContent);
|
|
19815
20232
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19816
20233
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
|
|
@@ -19924,7 +20341,7 @@ function evaluateInitiativeCompletion(dir, ini) {
|
|
|
19924
20341
|
// ../cli/src/core/resource-write.ts
|
|
19925
20342
|
import fs6 from "fs";
|
|
19926
20343
|
import path5 from "path";
|
|
19927
|
-
import
|
|
20344
|
+
import matter15 from "gray-matter";
|
|
19928
20345
|
var RESOURCES_DIR = "knowledge/tech/resources";
|
|
19929
20346
|
var ResourceWriteError = class extends Error {
|
|
19930
20347
|
constructor(code, message) {
|
|
@@ -20004,7 +20421,7 @@ function buildFrontmatter(id, input, base = {}) {
|
|
|
20004
20421
|
return data;
|
|
20005
20422
|
}
|
|
20006
20423
|
function serialize(data, body) {
|
|
20007
|
-
return
|
|
20424
|
+
return matter15.stringify(`
|
|
20008
20425
|
${body.trim()}
|
|
20009
20426
|
`, data);
|
|
20010
20427
|
}
|
|
@@ -20044,7 +20461,7 @@ function updateResource(dir, id, input) {
|
|
|
20044
20461
|
throw new ResourceWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
|
|
20045
20462
|
}
|
|
20046
20463
|
const raw = readFile(match.filePath);
|
|
20047
|
-
const parsed =
|
|
20464
|
+
const parsed = matter15(raw);
|
|
20048
20465
|
const base = parsed.data ?? {};
|
|
20049
20466
|
const data = buildFrontmatter(match.id || id, input, base);
|
|
20050
20467
|
let body = parsed.content.trim();
|
|
@@ -20082,6 +20499,17 @@ function deleteResource(dir, id, opts = {}) {
|
|
|
20082
20499
|
// src/tools.ts
|
|
20083
20500
|
var ok = (data) => ({ ok: true, data });
|
|
20084
20501
|
var fail = (message) => ({ ok: false, message });
|
|
20502
|
+
function preparePocReportTool(root) {
|
|
20503
|
+
const context = buildPocReportContext2(root);
|
|
20504
|
+
return context.eligible ? ok(context) : fail(context.handoff);
|
|
20505
|
+
}
|
|
20506
|
+
function persistPocReportTool(root, proposal, confirm2, force) {
|
|
20507
|
+
try {
|
|
20508
|
+
return ok(persistPocReport(root, proposal, { confirm: confirm2, force }));
|
|
20509
|
+
} catch (err) {
|
|
20510
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
20511
|
+
}
|
|
20512
|
+
}
|
|
20085
20513
|
function projectStatus(root) {
|
|
20086
20514
|
const explain = readJson(root, ".kaddo/explain.json");
|
|
20087
20515
|
const hints = readJson(root, ".kaddo/graph-hints.json");
|
|
@@ -20174,12 +20602,12 @@ function markWorkItemReady(root, id, confirm2) {
|
|
|
20174
20602
|
}
|
|
20175
20603
|
const raw = readText(root, item.path);
|
|
20176
20604
|
if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
|
|
20177
|
-
const parsed =
|
|
20605
|
+
const parsed = matter16(raw);
|
|
20178
20606
|
const data = { ...parsed.data };
|
|
20179
20607
|
const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
20180
20608
|
data.status = "ready";
|
|
20181
20609
|
data.ready_at = today3;
|
|
20182
|
-
const newContent =
|
|
20610
|
+
const newContent = matter16.stringify(parsed.content, data);
|
|
20183
20611
|
const posixPath = item.path.replace(/\\/g, "/");
|
|
20184
20612
|
let newRelPath = item.path;
|
|
20185
20613
|
if (posixPath.includes("/work-items/draft/")) {
|
|
@@ -20241,12 +20669,12 @@ function listGraphHints(root, filter = {}) {
|
|
|
20241
20669
|
});
|
|
20242
20670
|
}
|
|
20243
20671
|
function importWorkItemTool(root, args) {
|
|
20244
|
-
const
|
|
20672
|
+
const source3 = args.source === "cli" || args.source === "admin" ? args.source : "chat";
|
|
20245
20673
|
const onConflict = args.onConflict === "replace" || args.onConflict === "new-id" ? args.onConflict : void 0;
|
|
20246
20674
|
try {
|
|
20247
20675
|
const result = importWorkItem(root, {
|
|
20248
20676
|
content: args.content,
|
|
20249
|
-
source,
|
|
20677
|
+
source: source3,
|
|
20250
20678
|
type: args.type,
|
|
20251
20679
|
onConflict
|
|
20252
20680
|
});
|
|
@@ -20407,7 +20835,7 @@ function deleteResourceTool(root, id, confirm2) {
|
|
|
20407
20835
|
// src/multirepo.ts
|
|
20408
20836
|
import fs7 from "fs";
|
|
20409
20837
|
import path6 from "path";
|
|
20410
|
-
import
|
|
20838
|
+
import matter17 from "gray-matter";
|
|
20411
20839
|
import { parse as parseYaml11, stringify as stringifyYaml8 } from "yaml";
|
|
20412
20840
|
var ok2 = (data) => ({ ok: true, data });
|
|
20413
20841
|
var fail2 = (message) => ({ ok: false, message });
|
|
@@ -20559,7 +20987,7 @@ function validateWorkItemModulesTool(root, args) {
|
|
|
20559
20987
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20560
20988
|
const raw = readText(root, wi.path);
|
|
20561
20989
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20562
|
-
const parsed =
|
|
20990
|
+
const parsed = matter17(raw);
|
|
20563
20991
|
const data = parsed.data;
|
|
20564
20992
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20565
20993
|
const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
|
|
@@ -20612,7 +21040,7 @@ function getWorkItemContextTool(root, args) {
|
|
|
20612
21040
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20613
21041
|
const raw = readText(root, wi.path);
|
|
20614
21042
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20615
|
-
const parsed =
|
|
21043
|
+
const parsed = matter17(raw);
|
|
20616
21044
|
const data = parsed.data;
|
|
20617
21045
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20618
21046
|
const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
|
|
@@ -20698,7 +21126,7 @@ function suggestBranchStrategyTool(root, args) {
|
|
|
20698
21126
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20699
21127
|
const raw = readText(root, wi.path);
|
|
20700
21128
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20701
|
-
const parsed =
|
|
21129
|
+
const parsed = matter17(raw);
|
|
20702
21130
|
const data = parsed.data;
|
|
20703
21131
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20704
21132
|
const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
|
|
@@ -21307,10 +21735,10 @@ function getSystemNeighbors(dir, nodeId, opts = {}) {
|
|
|
21307
21735
|
}
|
|
21308
21736
|
return { seed: nodeId, nodes, relationships: rels, truncated };
|
|
21309
21737
|
}
|
|
21310
|
-
function findSystemPaths(dir,
|
|
21738
|
+
function findSystemPaths(dir, source3, target, opts = {}) {
|
|
21311
21739
|
const projection = getSystemMapProjection2(dir);
|
|
21312
21740
|
const byId = new Map(projection.nodes.map((n) => [n.id, n]));
|
|
21313
|
-
if (!byId.has(
|
|
21741
|
+
if (!byId.has(source3) || !byId.has(target)) return [];
|
|
21314
21742
|
const maxDepth = Math.max(1, opts.maxDepth ?? 4);
|
|
21315
21743
|
const out = /* @__PURE__ */ new Map();
|
|
21316
21744
|
for (const r2 of projection.relationships) {
|
|
@@ -21330,7 +21758,7 @@ function findSystemPaths(dir, source, target, opts = {}) {
|
|
|
21330
21758
|
walk(nxt, [...path9, nxt]);
|
|
21331
21759
|
}
|
|
21332
21760
|
};
|
|
21333
|
-
walk(
|
|
21761
|
+
walk(source3, [source3]);
|
|
21334
21762
|
return paths;
|
|
21335
21763
|
}
|
|
21336
21764
|
function getImpactCandidates(dir, seedIds, opts = {}) {
|
|
@@ -21458,14 +21886,14 @@ function systemImpactCandidatesTool(root, args) {
|
|
|
21458
21886
|
}
|
|
21459
21887
|
|
|
21460
21888
|
// ../cli/src/services/integrations.ts
|
|
21461
|
-
import
|
|
21889
|
+
import matter19 from "gray-matter";
|
|
21462
21890
|
import { parse as parseYaml13, stringify as stringifyYaml10 } from "yaml";
|
|
21463
21891
|
|
|
21464
21892
|
// ../cli/src/core/work-item-write.ts
|
|
21465
21893
|
import fs9 from "fs";
|
|
21466
21894
|
import path8 from "path";
|
|
21467
21895
|
import crypto4 from "crypto";
|
|
21468
|
-
import
|
|
21896
|
+
import matter18 from "gray-matter";
|
|
21469
21897
|
var SECTION_ORDER2 = [
|
|
21470
21898
|
"Actor",
|
|
21471
21899
|
"Outcome",
|
|
@@ -22513,7 +22941,7 @@ async function integrationsWorkItemTool(root, args) {
|
|
|
22513
22941
|
}
|
|
22514
22942
|
|
|
22515
22943
|
// ../cli/src/core/context-pack.ts
|
|
22516
|
-
import
|
|
22944
|
+
import matter20 from "gray-matter";
|
|
22517
22945
|
var CONTEXT_PACK_VERSION = "1";
|
|
22518
22946
|
var ARCH_DIR4 = "knowledge";
|
|
22519
22947
|
function readScanJson(dir) {
|
|
@@ -22526,7 +22954,7 @@ function readScanJson(dir) {
|
|
|
22526
22954
|
}
|
|
22527
22955
|
}
|
|
22528
22956
|
function firstParagraph4(markdown) {
|
|
22529
|
-
const body =
|
|
22957
|
+
const body = matter20(markdown).content.trim();
|
|
22530
22958
|
const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
|
|
22531
22959
|
return para ?? "";
|
|
22532
22960
|
}
|
|
@@ -23939,6 +24367,24 @@ function createServer(root) {
|
|
|
23939
24367
|
},
|
|
23940
24368
|
async (args) => toolText(guarded(root, () => markWorkItemReady(root, args.id, args.confirm)))
|
|
23941
24369
|
);
|
|
24370
|
+
server.registerTool(
|
|
24371
|
+
"kaddo_prepare_poc_report",
|
|
24372
|
+
{
|
|
24373
|
+
title: "Prepare final POC report context",
|
|
24374
|
+
description: "Build a deterministic, source-attributed POC report context. Does not call an LLM or write a report.",
|
|
24375
|
+
inputSchema: {}
|
|
24376
|
+
},
|
|
24377
|
+
async () => toolText(guarded(root, () => preparePocReportTool(root)))
|
|
24378
|
+
);
|
|
24379
|
+
server.registerTool(
|
|
24380
|
+
"kaddo_persist_poc_report",
|
|
24381
|
+
{
|
|
24382
|
+
title: "Persist confirmed final POC report",
|
|
24383
|
+
description: "Preview a proposed final POC report by default. With confirm=true, persist the next immutable version. Never provide credential values.",
|
|
24384
|
+
inputSchema: { proposal: z4.string(), confirm: z4.boolean().optional(), force: z4.boolean().optional() }
|
|
24385
|
+
},
|
|
24386
|
+
async (args) => toolText(guarded(root, () => persistPocReportTool(root, args.proposal, args.confirm, args.force)))
|
|
24387
|
+
);
|
|
23942
24388
|
server.registerTool(
|
|
23943
24389
|
"kaddo_list_resources",
|
|
23944
24390
|
{ title: "List Project Resources", description: "List external systems the project uses (databases, cloud, APIs\u2026), read-only, no secrets.", inputSchema: {} },
|
package/package.json
CHANGED