@kaddo/mcp 3.118.0 → 3.120.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 +530 -89
- 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);
|
|
@@ -4476,7 +4591,11 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
4476
4591
|
if (poc.conclusion === "pending") {
|
|
4477
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." };
|
|
4478
4593
|
}
|
|
4479
|
-
|
|
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." };
|
|
4480
4599
|
}
|
|
4481
4600
|
const q3 = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
4482
4601
|
const resolveAgent = (agent) => {
|
|
@@ -4914,7 +5033,7 @@ function deriveDeliveryCompletedStatus(dir) {
|
|
|
4914
5033
|
}
|
|
4915
5034
|
|
|
4916
5035
|
// ../cli/src/core/assets.ts
|
|
4917
|
-
import
|
|
5036
|
+
import matter9 from "gray-matter";
|
|
4918
5037
|
|
|
4919
5038
|
// ../cli/src/core/version.ts
|
|
4920
5039
|
import { fileURLToPath } from "url";
|
|
@@ -5590,6 +5709,50 @@ their intersection with the affected areas, and recommended actions.
|
|
|
5590
5709
|
- The assessment is proportional \u2014 a small change gets a brief assessment.
|
|
5591
5710
|
`
|
|
5592
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
|
+
);
|
|
5593
5756
|
var SKILLS = [
|
|
5594
5757
|
ADR_WRITING,
|
|
5595
5758
|
WORK_ITEM_REFINEMENT,
|
|
@@ -5600,10 +5763,11 @@ var SKILLS = [
|
|
|
5600
5763
|
IMPLEMENTATION_PLANNING,
|
|
5601
5764
|
MODULE_CONTEXT_REFINEMENT,
|
|
5602
5765
|
EVIDENCE_VERIFICATION,
|
|
5603
|
-
LEGACY_RISK_ASSESSMENT
|
|
5766
|
+
LEGACY_RISK_ASSESSMENT,
|
|
5767
|
+
POC_REPORT_WRITING
|
|
5604
5768
|
];
|
|
5605
5769
|
var SKILL_GROUPS = {
|
|
5606
|
-
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"],
|
|
5607
5771
|
tech: ["adr-writing", "ownership-suggestion", "graph-metadata-review", "module-context-refinement"],
|
|
5608
5772
|
integration: ["capsule-writing"]
|
|
5609
5773
|
};
|
|
@@ -8137,7 +8301,7 @@ function canonicalSkills() {
|
|
|
8137
8301
|
}
|
|
8138
8302
|
function versionOf(content) {
|
|
8139
8303
|
try {
|
|
8140
|
-
const v =
|
|
8304
|
+
const v = matter9(content).data.version;
|
|
8141
8305
|
return v === void 0 || v === null ? null : String(v);
|
|
8142
8306
|
} catch {
|
|
8143
8307
|
return null;
|
|
@@ -8399,9 +8563,9 @@ function parseWorkItemSource(frontmatter) {
|
|
|
8399
8563
|
reason: "No source metadata found."
|
|
8400
8564
|
};
|
|
8401
8565
|
}
|
|
8402
|
-
function renderSourceCompact(
|
|
8403
|
-
const parts = [
|
|
8404
|
-
if (
|
|
8566
|
+
function renderSourceCompact(source3) {
|
|
8567
|
+
const parts = [source3.type];
|
|
8568
|
+
if (source3.id) parts.push(source3.id);
|
|
8405
8569
|
return parts.join(" \xB7 ");
|
|
8406
8570
|
}
|
|
8407
8571
|
function optStr2(v) {
|
|
@@ -8778,7 +8942,17 @@ var concludePoc = {
|
|
|
8778
8942
|
return conclusion === "pending" ? { status: "pending", reason: "Record validated, rejected, or inconclusive from the evidence." } : { status: "done", evidence: [POC_ARTIFACT_PATH], reason: `Conclusion: ${conclusion}.` };
|
|
8779
8943
|
}
|
|
8780
8944
|
};
|
|
8781
|
-
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];
|
|
8782
8956
|
function stepsForState(state) {
|
|
8783
8957
|
switch (state) {
|
|
8784
8958
|
case "new":
|
|
@@ -8882,6 +9056,7 @@ function mapNextStepId(id) {
|
|
|
8882
9056
|
"poc-resources": "poc-resources",
|
|
8883
9057
|
"poc-create-experiment": "poc-create-experiment",
|
|
8884
9058
|
"poc-evaluate": "poc-evaluate",
|
|
9059
|
+
"poc-final-report": "poc-final-report",
|
|
8885
9060
|
"poc-complete": "poc-evaluate"
|
|
8886
9061
|
};
|
|
8887
9062
|
return MAP[id] ?? id;
|
|
@@ -8896,7 +9071,7 @@ function buildProjectRoute(dir) {
|
|
|
8896
9071
|
return { id: d3.id, label: d3.label, ...result };
|
|
8897
9072
|
});
|
|
8898
9073
|
for (const s of steps) {
|
|
8899
|
-
if (s.id === ctx.nextStepId && s.status !== "done") {
|
|
9074
|
+
if (s.id === ctx.nextStepId && s.status !== "done" && s.status !== "optional") {
|
|
8900
9075
|
s.status = "current";
|
|
8901
9076
|
}
|
|
8902
9077
|
}
|
|
@@ -8908,8 +9083,8 @@ function buildProjectRoute(dir) {
|
|
|
8908
9083
|
break;
|
|
8909
9084
|
}
|
|
8910
9085
|
}
|
|
8911
|
-
const completed = steps.filter((s) => s.status === "done").length;
|
|
8912
|
-
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;
|
|
8913
9088
|
const progressPercent = total > 0 ? Math.round(completed / total * 100) : 0;
|
|
8914
9089
|
return {
|
|
8915
9090
|
type: state,
|
|
@@ -8966,7 +9141,7 @@ function renderRouteCompact(route) {
|
|
|
8966
9141
|
}
|
|
8967
9142
|
|
|
8968
9143
|
// ../cli/src/core/metadata-health.ts
|
|
8969
|
-
import
|
|
9144
|
+
import matter10 from "gray-matter";
|
|
8970
9145
|
var KNOWLEDGE_FILES2 = [
|
|
8971
9146
|
"knowledge/business/business.md",
|
|
8972
9147
|
"knowledge/product/product.md",
|
|
@@ -8986,7 +9161,7 @@ function analyzeMetadataHealth(dir) {
|
|
|
8986
9161
|
let data;
|
|
8987
9162
|
try {
|
|
8988
9163
|
const raw = readFile(p);
|
|
8989
|
-
data =
|
|
9164
|
+
data = matter10(raw).data;
|
|
8990
9165
|
} catch {
|
|
8991
9166
|
continue;
|
|
8992
9167
|
}
|
|
@@ -9018,7 +9193,7 @@ function analyzeMetadataHealth(dir) {
|
|
|
9018
9193
|
if (isFile(p) && entry.endsWith(".md")) {
|
|
9019
9194
|
try {
|
|
9020
9195
|
const raw = readFile(p);
|
|
9021
|
-
const fm =
|
|
9196
|
+
const fm = matter10(raw).data;
|
|
9022
9197
|
if (fm.status === "done") {
|
|
9023
9198
|
const rel = p.replace(dir + "/", "").replace(dir + "\\", "").replace(/\\/g, "/");
|
|
9024
9199
|
findings.push({
|
|
@@ -9973,7 +10148,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
9973
10148
|
}
|
|
9974
10149
|
}
|
|
9975
10150
|
try {
|
|
9976
|
-
const body =
|
|
10151
|
+
const body = matter11(readFile(wi.filePath)).content;
|
|
9977
10152
|
if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
|
|
9978
10153
|
else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
|
|
9979
10154
|
if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
|
|
@@ -10381,7 +10556,7 @@ function parseRatioNumerator(s) {
|
|
|
10381
10556
|
}
|
|
10382
10557
|
var SAVINGS_DISCLAIMER = "These are evidence-based estimates, not exact ROI. Adjust assumptions with real team data.";
|
|
10383
10558
|
function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
10384
|
-
const { assumptions: a2, source } = loadAssumptions(dir);
|
|
10559
|
+
const { assumptions: a2, source: source3 } = loadAssumptions(dir);
|
|
10385
10560
|
const impact = buildImpactReport(dir, { scope: opts.scope, scopeSource: opts.scopeSource }, now);
|
|
10386
10561
|
const completed = impact.traceability.completed_work_items;
|
|
10387
10562
|
const wiWithOwnership = parseRatioNumerator(impact.traceability.work_items_connected_to_code);
|
|
@@ -10421,7 +10596,7 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10421
10596
|
if (impact.score === null || impact.score < 60 || graphQuality === "empty") {
|
|
10422
10597
|
level = "Low";
|
|
10423
10598
|
reasons.push("Impact score is low or the knowledge graph is empty.");
|
|
10424
|
-
} else if (impact.score >= 85 && graphQuality === "good" &&
|
|
10599
|
+
} else if (impact.score >= 85 && graphQuality === "good" && source3 === "file" && guard.available && guard.resolved > 0) {
|
|
10425
10600
|
level = "High";
|
|
10426
10601
|
reasons.push("Strong evidence, custom assumptions and recorded drift resolution.");
|
|
10427
10602
|
} else {
|
|
@@ -10435,9 +10610,9 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10435
10610
|
} else {
|
|
10436
10611
|
reasons.push("Guard history available, but no resolved drift warnings have been recorded yet.");
|
|
10437
10612
|
}
|
|
10438
|
-
reasons.push(
|
|
10613
|
+
reasons.push(source3 === "file" ? "Assumptions come from `.kaddo/savings.yml`." : "Savings assumptions are defaults and should be calibrated.");
|
|
10439
10614
|
const actions = [];
|
|
10440
|
-
if (
|
|
10615
|
+
if (source3 === "default") actions.push("Run `kaddo savings init` to customize assumptions.");
|
|
10441
10616
|
actions.push("Calibrate hourly cost and team assumptions with real team data.");
|
|
10442
10617
|
actions.push("Run `kaddo impact` to verify the underlying evidence quality.");
|
|
10443
10618
|
actions.push("Use this report as directional evidence, not accounting data.");
|
|
@@ -10455,7 +10630,7 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10455
10630
|
scope: impact.scope,
|
|
10456
10631
|
currency: a2.currency,
|
|
10457
10632
|
disclaimer: SAVINGS_DISCLAIMER,
|
|
10458
|
-
assumptions_source:
|
|
10633
|
+
assumptions_source: source3,
|
|
10459
10634
|
assumptions: {
|
|
10460
10635
|
hourly_cost: a2.hourly_cost,
|
|
10461
10636
|
context_preparation_minutes_saved_per_work_item: a2.context_preparation_minutes_saved_per_work_item,
|
|
@@ -10686,7 +10861,7 @@ function serializeDriftJson(r2) {
|
|
|
10686
10861
|
}
|
|
10687
10862
|
|
|
10688
10863
|
// src/workitems.ts
|
|
10689
|
-
import
|
|
10864
|
+
import matter12 from "gray-matter";
|
|
10690
10865
|
var VALID_SOURCES2 = /* @__PURE__ */ new Set(["manual", "roadmap", "jira", "github", "notion", "xlsx", "csv", "api", "external", "unknown"]);
|
|
10691
10866
|
function optStr3(v) {
|
|
10692
10867
|
return typeof v === "string" && v.trim() ? v.trim() : void 0;
|
|
@@ -10777,7 +10952,7 @@ function listWorkItems(root) {
|
|
|
10777
10952
|
if (raw === null) continue;
|
|
10778
10953
|
let parsed;
|
|
10779
10954
|
try {
|
|
10780
|
-
parsed =
|
|
10955
|
+
parsed = matter12(raw);
|
|
10781
10956
|
} catch {
|
|
10782
10957
|
continue;
|
|
10783
10958
|
}
|
|
@@ -10786,8 +10961,8 @@ function listWorkItems(root) {
|
|
|
10786
10961
|
const status = lifecycleOf(data, rel);
|
|
10787
10962
|
const code = strArray2(data.code);
|
|
10788
10963
|
const domains = strArray2(data.domains);
|
|
10789
|
-
const
|
|
10790
|
-
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;
|
|
10791
10966
|
items.push({
|
|
10792
10967
|
id: String(data.id ?? "") || rel,
|
|
10793
10968
|
title: String(data.title ?? ""),
|
|
@@ -10800,7 +10975,7 @@ function listWorkItems(root) {
|
|
|
10800
10975
|
capabilities: strArray2(data.capabilities),
|
|
10801
10976
|
decisions: strArray2(data.decisions),
|
|
10802
10977
|
capsules: strArray2(data.capsules),
|
|
10803
|
-
source,
|
|
10978
|
+
source: source3,
|
|
10804
10979
|
domains,
|
|
10805
10980
|
readiness,
|
|
10806
10981
|
...data.implementation_status ? { implementation_status: String(data.implementation_status) } : {},
|
|
@@ -10817,7 +10992,7 @@ function listWorkItems(root) {
|
|
|
10817
10992
|
}
|
|
10818
10993
|
return items.sort((a2, b2) => a2.id.localeCompare(b2.id));
|
|
10819
10994
|
}
|
|
10820
|
-
function assessReadiness(data, content, code, domains,
|
|
10995
|
+
function assessReadiness(data, content, code, domains, source3) {
|
|
10821
10996
|
const body = content.trim();
|
|
10822
10997
|
const lower = body.toLowerCase();
|
|
10823
10998
|
const warnings = [];
|
|
@@ -10974,6 +11149,25 @@ var RESOURCES = [
|
|
|
10974
11149
|
"POC artifact not found. Initialize in POC mode or switch with `kaddo project mode poc`."
|
|
10975
11150
|
)
|
|
10976
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
|
+
},
|
|
10977
11171
|
{
|
|
10978
11172
|
uri: "kaddo://capsules",
|
|
10979
11173
|
name: "Kaddo knowledge capsules",
|
|
@@ -11257,14 +11451,14 @@ function getPrompt(root, name) {
|
|
|
11257
11451
|
}
|
|
11258
11452
|
|
|
11259
11453
|
// src/tools.ts
|
|
11260
|
-
import
|
|
11454
|
+
import matter16 from "gray-matter";
|
|
11261
11455
|
|
|
11262
11456
|
// ../cli/dist/core.js
|
|
11263
11457
|
import fs3 from "fs";
|
|
11264
11458
|
import path3 from "path";
|
|
11265
11459
|
import { z as z3 } from "zod";
|
|
11266
11460
|
import { parse as parseYaml10, stringify as stringifyYaml5 } from "yaml";
|
|
11267
|
-
import
|
|
11461
|
+
import matter13 from "gray-matter";
|
|
11268
11462
|
import matter22 from "gray-matter";
|
|
11269
11463
|
import { parse as parseYaml62, stringify as stringifyYaml42 } from "yaml";
|
|
11270
11464
|
import { parse as parseYaml22 } from "yaml";
|
|
@@ -11275,25 +11469,27 @@ import matter32 from "gray-matter";
|
|
|
11275
11469
|
import matter42 from "gray-matter";
|
|
11276
11470
|
import matter52 from "gray-matter";
|
|
11277
11471
|
import { parse as parseYaml72 } from "yaml";
|
|
11472
|
+
import { createHash as createHash2 } from "crypto";
|
|
11278
11473
|
import matter62 from "gray-matter";
|
|
11474
|
+
import matter72 from "gray-matter";
|
|
11279
11475
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11280
11476
|
import { dirname as dirname2, join as join22, parse as parse2 } from "path";
|
|
11281
11477
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
11282
|
-
import matter72 from "gray-matter";
|
|
11283
11478
|
import matter82 from "gray-matter";
|
|
11284
11479
|
import matter92 from "gray-matter";
|
|
11480
|
+
import matter102 from "gray-matter";
|
|
11285
11481
|
import { parse as parseYaml82, stringify as stringifyYaml52 } from "yaml";
|
|
11286
11482
|
import fs4 from "fs";
|
|
11287
11483
|
import path4 from "path";
|
|
11288
11484
|
import crypto2 from "crypto";
|
|
11289
|
-
import
|
|
11485
|
+
import matter112 from "gray-matter";
|
|
11290
11486
|
import crypto3 from "crypto";
|
|
11291
11487
|
import fs5 from "fs";
|
|
11292
|
-
import matter112 from "gray-matter";
|
|
11293
11488
|
import matter122 from "gray-matter";
|
|
11489
|
+
import matter132 from "gray-matter";
|
|
11294
11490
|
import { parse as parseYaml92, stringify as stringifyYaml6 } from "yaml";
|
|
11295
11491
|
import { parse as parseYaml102, stringify as stringifyYaml7 } from "yaml";
|
|
11296
|
-
import
|
|
11492
|
+
import matter14 from "gray-matter";
|
|
11297
11493
|
function exists2(p) {
|
|
11298
11494
|
return fs3.existsSync(p);
|
|
11299
11495
|
}
|
|
@@ -11469,7 +11665,7 @@ function parseImpactAnalysis2(data) {
|
|
|
11469
11665
|
}
|
|
11470
11666
|
function parseArtifact2(filePath, raw) {
|
|
11471
11667
|
try {
|
|
11472
|
-
const { data } =
|
|
11668
|
+
const { data } = matter13(raw);
|
|
11473
11669
|
const globs = Array.isArray(data.code) ? data.code.filter(Boolean) : [];
|
|
11474
11670
|
return {
|
|
11475
11671
|
filePath,
|
|
@@ -11583,12 +11779,12 @@ function classifyLayer2(posixPath) {
|
|
|
11583
11779
|
return "unknown";
|
|
11584
11780
|
}
|
|
11585
11781
|
function enrich2(dir, a2) {
|
|
11586
|
-
const
|
|
11782
|
+
const posix22 = toPosix7(a2.filePath);
|
|
11587
11783
|
const isWi = isWorkItemArtifact2(a2);
|
|
11588
11784
|
return {
|
|
11589
11785
|
...a2,
|
|
11590
11786
|
relPath: relativeTo2(dir, a2.filePath),
|
|
11591
|
-
layer: classifyLayer2(
|
|
11787
|
+
layer: classifyLayer2(posix22),
|
|
11592
11788
|
isWorkItem: isWi,
|
|
11593
11789
|
lifecycle: isWi ? lifecycleStateOf2({ status: a2.status, filePath: a2.filePath }) : void 0
|
|
11594
11790
|
};
|
|
@@ -13618,6 +13814,177 @@ function buildTechDecisions2(dir) {
|
|
|
13618
13814
|
function hasUnmaterializedDecisions(td) {
|
|
13619
13815
|
return td.candidates > 0 && td.adrs === 0;
|
|
13620
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
|
+
}
|
|
13621
13988
|
function resolveVersion2() {
|
|
13622
13989
|
try {
|
|
13623
13990
|
let dir = dirname2(fileURLToPath2(import.meta.url));
|
|
@@ -14286,6 +14653,50 @@ their intersection with the affected areas, and recommended actions.
|
|
|
14286
14653
|
- The assessment is proportional \u2014 a small change gets a brief assessment.
|
|
14287
14654
|
`
|
|
14288
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
|
+
);
|
|
14289
14700
|
var SKILLS2 = [
|
|
14290
14701
|
ADR_WRITING2,
|
|
14291
14702
|
WORK_ITEM_REFINEMENT2,
|
|
@@ -14296,10 +14707,11 @@ var SKILLS2 = [
|
|
|
14296
14707
|
IMPLEMENTATION_PLANNING2,
|
|
14297
14708
|
MODULE_CONTEXT_REFINEMENT2,
|
|
14298
14709
|
EVIDENCE_VERIFICATION2,
|
|
14299
|
-
LEGACY_RISK_ASSESSMENT2
|
|
14710
|
+
LEGACY_RISK_ASSESSMENT2,
|
|
14711
|
+
POC_REPORT_WRITING2
|
|
14300
14712
|
];
|
|
14301
14713
|
var SKILL_GROUPS2 = {
|
|
14302
|
-
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"],
|
|
14303
14715
|
tech: ["adr-writing", "ownership-suggestion", "graph-metadata-review", "module-context-refinement"],
|
|
14304
14716
|
integration: ["capsule-writing"]
|
|
14305
14717
|
};
|
|
@@ -17547,7 +17959,7 @@ function parseOriginalSnapshot(fm) {
|
|
|
17547
17959
|
function readBody(filePath) {
|
|
17548
17960
|
try {
|
|
17549
17961
|
const raw = readFile2(filePath);
|
|
17550
|
-
return
|
|
17962
|
+
return matter102(raw).content;
|
|
17551
17963
|
} catch {
|
|
17552
17964
|
return "";
|
|
17553
17965
|
}
|
|
@@ -17832,7 +18244,7 @@ function ensureH1(body, title) {
|
|
|
17832
18244
|
${trimmed}`;
|
|
17833
18245
|
}
|
|
17834
18246
|
function serialize2(data, body) {
|
|
17835
|
-
return
|
|
18247
|
+
return matter112.stringify(`
|
|
17836
18248
|
${body.trim()}
|
|
17837
18249
|
`, data);
|
|
17838
18250
|
}
|
|
@@ -17867,7 +18279,7 @@ function createWorkItem(dir, opts) {
|
|
|
17867
18279
|
const title = intent.split(/\r?\n/)[0].trim().slice(0, 120);
|
|
17868
18280
|
const today22 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
17869
18281
|
const summary = opts.summary?.trim() || intent;
|
|
17870
|
-
const
|
|
18282
|
+
const source22 = opts.source ? { ...opts.source, inferred: false } : { type: "manual", inferred: false };
|
|
17871
18283
|
const data = {
|
|
17872
18284
|
type,
|
|
17873
18285
|
id,
|
|
@@ -17875,7 +18287,7 @@ function createWorkItem(dir, opts) {
|
|
|
17875
18287
|
status: "draft",
|
|
17876
18288
|
work_type: type,
|
|
17877
18289
|
created_at: today22,
|
|
17878
|
-
source,
|
|
18290
|
+
source: source22,
|
|
17879
18291
|
generated_by: "kaddo-admin",
|
|
17880
18292
|
affected_modules: [],
|
|
17881
18293
|
summary
|
|
@@ -18623,7 +19035,7 @@ function detectFormat(raw) {
|
|
|
18623
19035
|
if (!trimmed) throw new ImportError("EMPTY_INPUT", "Import content is empty.");
|
|
18624
19036
|
if (trimmed.startsWith("---")) {
|
|
18625
19037
|
try {
|
|
18626
|
-
const { data } =
|
|
19038
|
+
const { data } = matter122(trimmed);
|
|
18627
19039
|
if (data && typeof data === "object" && Object.keys(data).length > 0) {
|
|
18628
19040
|
const d3 = data;
|
|
18629
19041
|
if (d3.type === "work-item" || d3.id && typeof d3.id === "string" && /^WI-\d+/.test(d3.id)) {
|
|
@@ -18645,7 +19057,7 @@ function parseContent(raw) {
|
|
|
18645
19057
|
let bodyText = trimmed;
|
|
18646
19058
|
if (format === "kaddo-frontmatter" || format === "markdown-frontmatter") {
|
|
18647
19059
|
try {
|
|
18648
|
-
const parsed =
|
|
19060
|
+
const parsed = matter122(trimmed);
|
|
18649
19061
|
frontmatterFields = parsed.data ?? {};
|
|
18650
19062
|
bodyText = parsed.content.trim();
|
|
18651
19063
|
} catch {
|
|
@@ -18717,12 +19129,12 @@ function normalizeImportFields(parsed, opts) {
|
|
|
18717
19129
|
const intent = parsed.title;
|
|
18718
19130
|
const body = hasRealBody ? parsed.body : void 0;
|
|
18719
19131
|
const summary = hasRealBody ? deriveShortSummary(parsed) : parsed.title;
|
|
18720
|
-
const
|
|
19132
|
+
const source22 = {
|
|
18721
19133
|
type: opts.importSource === "chat" ? "chat" : "external",
|
|
18722
19134
|
imported_at: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
18723
19135
|
};
|
|
18724
|
-
|
|
18725
|
-
|
|
19136
|
+
source22.source_format = opts.sourceFormat;
|
|
19137
|
+
source22.source_hash = opts.sourceHash;
|
|
18726
19138
|
const snapshot = { title: parsed.title };
|
|
18727
19139
|
if (summary !== parsed.title) snapshot.description = summary;
|
|
18728
19140
|
if (typeof parsed.candidateFields.type === "string") snapshot.type = parsed.candidateFields.type;
|
|
@@ -18731,7 +19143,7 @@ function normalizeImportFields(parsed, opts) {
|
|
|
18731
19143
|
const labels = parsed.candidateFields.labels.filter((l2) => typeof l2 === "string");
|
|
18732
19144
|
if (labels.length > 0) snapshot.labels = labels;
|
|
18733
19145
|
}
|
|
18734
|
-
return { type, intent, summary, body, source, snapshot, discardedFields };
|
|
19146
|
+
return { type, intent, summary, body, source: source22, snapshot, discardedFields };
|
|
18735
19147
|
}
|
|
18736
19148
|
function computeContentHash(raw) {
|
|
18737
19149
|
return crypto3.createHash("sha256").update(raw.trim(), "utf-8").digest("hex");
|
|
@@ -18742,10 +19154,10 @@ function findDuplicateByHash(dir, hash) {
|
|
|
18742
19154
|
const raw = readFile2(item.filePath);
|
|
18743
19155
|
if (!raw) continue;
|
|
18744
19156
|
try {
|
|
18745
|
-
const { data } =
|
|
18746
|
-
const
|
|
18747
|
-
if (
|
|
18748
|
-
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;
|
|
18749
19161
|
if (s.source_hash === hash) {
|
|
18750
19162
|
return { workItemId: data.id ?? item.id ?? item.title, path: item.relPath };
|
|
18751
19163
|
}
|
|
@@ -19637,7 +20049,7 @@ function buildInitiativeContent(id, input) {
|
|
|
19637
20049
|
if (input.priority) data.priority = input.priority;
|
|
19638
20050
|
if (input.source) data.source = input.source;
|
|
19639
20051
|
if (input.sourceId) data.source_id = input.sourceId;
|
|
19640
|
-
return
|
|
20052
|
+
return matter14.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
|
|
19641
20053
|
}
|
|
19642
20054
|
function createInitiative(dir, input) {
|
|
19643
20055
|
if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
|
|
@@ -19661,10 +20073,10 @@ function transitionInitiative(dir, id, to) {
|
|
|
19661
20073
|
throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
|
|
19662
20074
|
}
|
|
19663
20075
|
const raw = readFile2(ini.filePath);
|
|
19664
|
-
const { data, content } =
|
|
20076
|
+
const { data, content } = matter14(raw, {});
|
|
19665
20077
|
data.status = to;
|
|
19666
20078
|
if (to === "completed") data.completed_at = today2();
|
|
19667
|
-
const newContent =
|
|
20079
|
+
const newContent = matter14.stringify(content, data);
|
|
19668
20080
|
writeFile2(ini.filePath, newContent);
|
|
19669
20081
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19670
20082
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
|
|
@@ -19674,7 +20086,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
19674
20086
|
const ini = getInitiative(dir, id);
|
|
19675
20087
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
19676
20088
|
const raw = readFile2(ini.filePath);
|
|
19677
|
-
const { data, content } =
|
|
20089
|
+
const { data, content } = matter14(raw, {});
|
|
19678
20090
|
const existing = Array.isArray(data.candidates) ? data.candidates : [];
|
|
19679
20091
|
const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
|
|
19680
20092
|
const normalized = normalizeCandidate(candidate, fallbackId);
|
|
@@ -19682,7 +20094,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
19682
20094
|
throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
|
|
19683
20095
|
}
|
|
19684
20096
|
data.candidates = [...existing, candidateToData(normalized)];
|
|
19685
|
-
const newContent =
|
|
20097
|
+
const newContent = matter14.stringify(content, data);
|
|
19686
20098
|
writeFile2(ini.filePath, newContent);
|
|
19687
20099
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19688
20100
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
|
|
@@ -19692,7 +20104,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
19692
20104
|
const ini = getInitiative(dir, id);
|
|
19693
20105
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
19694
20106
|
const raw = readFile2(ini.filePath);
|
|
19695
|
-
const { data, content } =
|
|
20107
|
+
const { data, content } = matter14(raw, {});
|
|
19696
20108
|
if (patch.title !== void 0) {
|
|
19697
20109
|
if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
|
|
19698
20110
|
data.title = patch.title.trim();
|
|
@@ -19702,7 +20114,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
19702
20114
|
if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
|
|
19703
20115
|
if (patch.domains !== void 0) data.domains = patch.domains;
|
|
19704
20116
|
if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
|
|
19705
|
-
const newContent =
|
|
20117
|
+
const newContent = matter14.stringify(content, data);
|
|
19706
20118
|
writeFile2(ini.filePath, newContent);
|
|
19707
20119
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19708
20120
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
|
|
@@ -19720,10 +20132,10 @@ function addExternalLink(dir, id, link) {
|
|
|
19720
20132
|
throw new InitiativeWriteError(`External link ${link.integration}:${link.externalId} already exists on ${id}.`);
|
|
19721
20133
|
}
|
|
19722
20134
|
const raw = readFile2(ini.filePath);
|
|
19723
|
-
const { data, content } =
|
|
20135
|
+
const { data, content } = matter14(raw, {});
|
|
19724
20136
|
const existing = Array.isArray(data.external_links) ? data.external_links : [];
|
|
19725
20137
|
data.external_links = [...existing, externalLinkToData(link)];
|
|
19726
|
-
const newContent =
|
|
20138
|
+
const newContent = matter14.stringify(content, data);
|
|
19727
20139
|
writeFile2(ini.filePath, newContent);
|
|
19728
20140
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19729
20141
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding an external link.");
|
|
@@ -19792,7 +20204,7 @@ function buildWorkItemContent(wiId, iniId, candidate) {
|
|
|
19792
20204
|
"_What did we learn? Update after completion._",
|
|
19793
20205
|
""
|
|
19794
20206
|
].join("\n");
|
|
19795
|
-
return
|
|
20207
|
+
return matter14.stringify("\n" + body, data);
|
|
19796
20208
|
}
|
|
19797
20209
|
function materializeCandidate(dir, id, candidateId) {
|
|
19798
20210
|
const ini = getInitiative(dir, id);
|
|
@@ -19809,13 +20221,13 @@ function materializeCandidate(dir, id, candidateId) {
|
|
|
19809
20221
|
ensureDir2(join3(dir, DRAFT_DIR));
|
|
19810
20222
|
writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
|
|
19811
20223
|
const raw = readFile2(ini.filePath);
|
|
19812
|
-
const { data, content } =
|
|
20224
|
+
const { data, content } = matter14(raw, {});
|
|
19813
20225
|
const cands = Array.isArray(data.candidates) ? data.candidates : [];
|
|
19814
20226
|
for (const c of cands) {
|
|
19815
20227
|
if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
|
|
19816
20228
|
}
|
|
19817
20229
|
data.candidates = cands;
|
|
19818
|
-
const newContent =
|
|
20230
|
+
const newContent = matter14.stringify(content, data);
|
|
19819
20231
|
writeFile2(ini.filePath, newContent);
|
|
19820
20232
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19821
20233
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
|
|
@@ -19929,7 +20341,7 @@ function evaluateInitiativeCompletion(dir, ini) {
|
|
|
19929
20341
|
// ../cli/src/core/resource-write.ts
|
|
19930
20342
|
import fs6 from "fs";
|
|
19931
20343
|
import path5 from "path";
|
|
19932
|
-
import
|
|
20344
|
+
import matter15 from "gray-matter";
|
|
19933
20345
|
var RESOURCES_DIR = "knowledge/tech/resources";
|
|
19934
20346
|
var ResourceWriteError = class extends Error {
|
|
19935
20347
|
constructor(code, message) {
|
|
@@ -20009,7 +20421,7 @@ function buildFrontmatter(id, input, base = {}) {
|
|
|
20009
20421
|
return data;
|
|
20010
20422
|
}
|
|
20011
20423
|
function serialize(data, body) {
|
|
20012
|
-
return
|
|
20424
|
+
return matter15.stringify(`
|
|
20013
20425
|
${body.trim()}
|
|
20014
20426
|
`, data);
|
|
20015
20427
|
}
|
|
@@ -20049,7 +20461,7 @@ function updateResource(dir, id, input) {
|
|
|
20049
20461
|
throw new ResourceWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
|
|
20050
20462
|
}
|
|
20051
20463
|
const raw = readFile(match.filePath);
|
|
20052
|
-
const parsed =
|
|
20464
|
+
const parsed = matter15(raw);
|
|
20053
20465
|
const base = parsed.data ?? {};
|
|
20054
20466
|
const data = buildFrontmatter(match.id || id, input, base);
|
|
20055
20467
|
let body = parsed.content.trim();
|
|
@@ -20087,6 +20499,17 @@ function deleteResource(dir, id, opts = {}) {
|
|
|
20087
20499
|
// src/tools.ts
|
|
20088
20500
|
var ok = (data) => ({ ok: true, data });
|
|
20089
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
|
+
}
|
|
20090
20513
|
function projectStatus(root) {
|
|
20091
20514
|
const explain = readJson(root, ".kaddo/explain.json");
|
|
20092
20515
|
const hints = readJson(root, ".kaddo/graph-hints.json");
|
|
@@ -20179,12 +20602,12 @@ function markWorkItemReady(root, id, confirm2) {
|
|
|
20179
20602
|
}
|
|
20180
20603
|
const raw = readText(root, item.path);
|
|
20181
20604
|
if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
|
|
20182
|
-
const parsed =
|
|
20605
|
+
const parsed = matter16(raw);
|
|
20183
20606
|
const data = { ...parsed.data };
|
|
20184
20607
|
const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
20185
20608
|
data.status = "ready";
|
|
20186
20609
|
data.ready_at = today3;
|
|
20187
|
-
const newContent =
|
|
20610
|
+
const newContent = matter16.stringify(parsed.content, data);
|
|
20188
20611
|
const posixPath = item.path.replace(/\\/g, "/");
|
|
20189
20612
|
let newRelPath = item.path;
|
|
20190
20613
|
if (posixPath.includes("/work-items/draft/")) {
|
|
@@ -20246,12 +20669,12 @@ function listGraphHints(root, filter = {}) {
|
|
|
20246
20669
|
});
|
|
20247
20670
|
}
|
|
20248
20671
|
function importWorkItemTool(root, args) {
|
|
20249
|
-
const
|
|
20672
|
+
const source3 = args.source === "cli" || args.source === "admin" ? args.source : "chat";
|
|
20250
20673
|
const onConflict = args.onConflict === "replace" || args.onConflict === "new-id" ? args.onConflict : void 0;
|
|
20251
20674
|
try {
|
|
20252
20675
|
const result = importWorkItem(root, {
|
|
20253
20676
|
content: args.content,
|
|
20254
|
-
source,
|
|
20677
|
+
source: source3,
|
|
20255
20678
|
type: args.type,
|
|
20256
20679
|
onConflict
|
|
20257
20680
|
});
|
|
@@ -20412,7 +20835,7 @@ function deleteResourceTool(root, id, confirm2) {
|
|
|
20412
20835
|
// src/multirepo.ts
|
|
20413
20836
|
import fs7 from "fs";
|
|
20414
20837
|
import path6 from "path";
|
|
20415
|
-
import
|
|
20838
|
+
import matter17 from "gray-matter";
|
|
20416
20839
|
import { parse as parseYaml11, stringify as stringifyYaml8 } from "yaml";
|
|
20417
20840
|
var ok2 = (data) => ({ ok: true, data });
|
|
20418
20841
|
var fail2 = (message) => ({ ok: false, message });
|
|
@@ -20564,7 +20987,7 @@ function validateWorkItemModulesTool(root, args) {
|
|
|
20564
20987
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20565
20988
|
const raw = readText(root, wi.path);
|
|
20566
20989
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20567
|
-
const parsed =
|
|
20990
|
+
const parsed = matter17(raw);
|
|
20568
20991
|
const data = parsed.data;
|
|
20569
20992
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20570
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) : [] })) : [];
|
|
@@ -20617,7 +21040,7 @@ function getWorkItemContextTool(root, args) {
|
|
|
20617
21040
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20618
21041
|
const raw = readText(root, wi.path);
|
|
20619
21042
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20620
|
-
const parsed =
|
|
21043
|
+
const parsed = matter17(raw);
|
|
20621
21044
|
const data = parsed.data;
|
|
20622
21045
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20623
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) : [] })) : [];
|
|
@@ -20703,7 +21126,7 @@ function suggestBranchStrategyTool(root, args) {
|
|
|
20703
21126
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20704
21127
|
const raw = readText(root, wi.path);
|
|
20705
21128
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20706
|
-
const parsed =
|
|
21129
|
+
const parsed = matter17(raw);
|
|
20707
21130
|
const data = parsed.data;
|
|
20708
21131
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20709
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) : [] })) : [];
|
|
@@ -21312,10 +21735,10 @@ function getSystemNeighbors(dir, nodeId, opts = {}) {
|
|
|
21312
21735
|
}
|
|
21313
21736
|
return { seed: nodeId, nodes, relationships: rels, truncated };
|
|
21314
21737
|
}
|
|
21315
|
-
function findSystemPaths(dir,
|
|
21738
|
+
function findSystemPaths(dir, source3, target, opts = {}) {
|
|
21316
21739
|
const projection = getSystemMapProjection2(dir);
|
|
21317
21740
|
const byId = new Map(projection.nodes.map((n) => [n.id, n]));
|
|
21318
|
-
if (!byId.has(
|
|
21741
|
+
if (!byId.has(source3) || !byId.has(target)) return [];
|
|
21319
21742
|
const maxDepth = Math.max(1, opts.maxDepth ?? 4);
|
|
21320
21743
|
const out = /* @__PURE__ */ new Map();
|
|
21321
21744
|
for (const r2 of projection.relationships) {
|
|
@@ -21335,7 +21758,7 @@ function findSystemPaths(dir, source, target, opts = {}) {
|
|
|
21335
21758
|
walk(nxt, [...path9, nxt]);
|
|
21336
21759
|
}
|
|
21337
21760
|
};
|
|
21338
|
-
walk(
|
|
21761
|
+
walk(source3, [source3]);
|
|
21339
21762
|
return paths;
|
|
21340
21763
|
}
|
|
21341
21764
|
function getImpactCandidates(dir, seedIds, opts = {}) {
|
|
@@ -21463,14 +21886,14 @@ function systemImpactCandidatesTool(root, args) {
|
|
|
21463
21886
|
}
|
|
21464
21887
|
|
|
21465
21888
|
// ../cli/src/services/integrations.ts
|
|
21466
|
-
import
|
|
21889
|
+
import matter19 from "gray-matter";
|
|
21467
21890
|
import { parse as parseYaml13, stringify as stringifyYaml10 } from "yaml";
|
|
21468
21891
|
|
|
21469
21892
|
// ../cli/src/core/work-item-write.ts
|
|
21470
21893
|
import fs9 from "fs";
|
|
21471
21894
|
import path8 from "path";
|
|
21472
21895
|
import crypto4 from "crypto";
|
|
21473
|
-
import
|
|
21896
|
+
import matter18 from "gray-matter";
|
|
21474
21897
|
var SECTION_ORDER2 = [
|
|
21475
21898
|
"Actor",
|
|
21476
21899
|
"Outcome",
|
|
@@ -22518,7 +22941,7 @@ async function integrationsWorkItemTool(root, args) {
|
|
|
22518
22941
|
}
|
|
22519
22942
|
|
|
22520
22943
|
// ../cli/src/core/context-pack.ts
|
|
22521
|
-
import
|
|
22944
|
+
import matter20 from "gray-matter";
|
|
22522
22945
|
var CONTEXT_PACK_VERSION = "1";
|
|
22523
22946
|
var ARCH_DIR4 = "knowledge";
|
|
22524
22947
|
function readScanJson(dir) {
|
|
@@ -22531,7 +22954,7 @@ function readScanJson(dir) {
|
|
|
22531
22954
|
}
|
|
22532
22955
|
}
|
|
22533
22956
|
function firstParagraph4(markdown) {
|
|
22534
|
-
const body =
|
|
22957
|
+
const body = matter20(markdown).content.trim();
|
|
22535
22958
|
const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
|
|
22536
22959
|
return para ?? "";
|
|
22537
22960
|
}
|
|
@@ -23944,6 +24367,24 @@ function createServer(root) {
|
|
|
23944
24367
|
},
|
|
23945
24368
|
async (args) => toolText(guarded(root, () => markWorkItemReady(root, args.id, args.confirm)))
|
|
23946
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
|
+
);
|
|
23947
24388
|
server.registerTool(
|
|
23948
24389
|
"kaddo_list_resources",
|
|
23949
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