@kaddo/mcp 3.114.0 → 3.115.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 +377 -56
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -386,7 +386,7 @@ function join(...parts) {
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
// ../cli/src/core/impact-report.ts
|
|
389
|
-
import
|
|
389
|
+
import matter10 from "gray-matter";
|
|
390
390
|
|
|
391
391
|
// ../cli/src/core/config.ts
|
|
392
392
|
import { z } from "zod";
|
|
@@ -2590,8 +2590,117 @@ function discoverInitiatives(dir) {
|
|
|
2590
2590
|
return out.sort((a2, b2) => a2.id.localeCompare(b2.id));
|
|
2591
2591
|
}
|
|
2592
2592
|
|
|
2593
|
+
// ../cli/src/core/resources.ts
|
|
2594
|
+
import matter6 from "gray-matter";
|
|
2595
|
+
var RESOURCE_ARTIFACT_TYPE = "project-resource";
|
|
2596
|
+
function str(v) {
|
|
2597
|
+
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
2598
|
+
}
|
|
2599
|
+
function strArray(v) {
|
|
2600
|
+
if (Array.isArray(v)) return v.map((x2) => String(x2).trim()).filter(Boolean);
|
|
2601
|
+
if (typeof v === "string" && v.trim()) return [v.trim()];
|
|
2602
|
+
return [];
|
|
2603
|
+
}
|
|
2604
|
+
function parseInterfaces(v) {
|
|
2605
|
+
if (!Array.isArray(v)) return [];
|
|
2606
|
+
const out = [];
|
|
2607
|
+
for (const raw of v) {
|
|
2608
|
+
if (!raw || typeof raw !== "object") continue;
|
|
2609
|
+
const o2 = raw;
|
|
2610
|
+
const type = str(o2.type);
|
|
2611
|
+
if (!type) continue;
|
|
2612
|
+
out.push({
|
|
2613
|
+
type,
|
|
2614
|
+
tool: str(o2.tool),
|
|
2615
|
+
provider: str(o2.provider),
|
|
2616
|
+
purpose: str(o2.purpose),
|
|
2617
|
+
operations: strArray(o2.operations),
|
|
2618
|
+
environments: strArray(o2.environments),
|
|
2619
|
+
constraints: str(o2.constraints)
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
return out;
|
|
2623
|
+
}
|
|
2624
|
+
function parseBoundaries(v) {
|
|
2625
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
2626
|
+
const out = {};
|
|
2627
|
+
for (const [env, val] of Object.entries(v)) {
|
|
2628
|
+
const list3 = strArray(val);
|
|
2629
|
+
if (list3.length > 0) out[env] = list3;
|
|
2630
|
+
}
|
|
2631
|
+
return out;
|
|
2632
|
+
}
|
|
2633
|
+
function parseAuth(v) {
|
|
2634
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return { mode: null, refs: [] };
|
|
2635
|
+
const o2 = v;
|
|
2636
|
+
const refs = strArray(o2.refs).filter((r2) => !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
|
|
2637
|
+
return { mode: str(o2.mode), refs };
|
|
2638
|
+
}
|
|
2639
|
+
function toDetail(filePath, relPath, raw) {
|
|
2640
|
+
const parsed = matter6(raw);
|
|
2641
|
+
const fm = parsed.data ?? {};
|
|
2642
|
+
const body = parsed.content.trim();
|
|
2643
|
+
const id = str(fm.id) ?? "";
|
|
2644
|
+
return {
|
|
2645
|
+
id,
|
|
2646
|
+
title: str(fm.title) ?? id,
|
|
2647
|
+
resourceType: str(fm.resource_type),
|
|
2648
|
+
provider: str(fm.provider),
|
|
2649
|
+
environments: strArray(fm.environments),
|
|
2650
|
+
path: relPath,
|
|
2651
|
+
purpose: str(fm.purpose),
|
|
2652
|
+
interfaces: parseInterfaces(fm.access_interfaces),
|
|
2653
|
+
boundaries: parseBoundaries(fm.access_boundaries),
|
|
2654
|
+
...(() => {
|
|
2655
|
+
const a2 = parseAuth(fm.authentication);
|
|
2656
|
+
return { authRefs: a2.refs, authMode: a2.mode };
|
|
2657
|
+
})(),
|
|
2658
|
+
markdownBody: body
|
|
2659
|
+
};
|
|
2660
|
+
}
|
|
2661
|
+
function getResources(dir) {
|
|
2662
|
+
return discoverKnowledge(dir).filter((a2) => a2.type === RESOURCE_ARTIFACT_TYPE).map((a2) => {
|
|
2663
|
+
const fm = a2.rawFrontmatter;
|
|
2664
|
+
return {
|
|
2665
|
+
id: a2.id || str(fm.id) || a2.title,
|
|
2666
|
+
title: a2.title || str(fm.title) || a2.id,
|
|
2667
|
+
resourceType: str(fm.resource_type),
|
|
2668
|
+
provider: str(fm.provider),
|
|
2669
|
+
environments: strArray(fm.environments),
|
|
2670
|
+
path: a2.relPath
|
|
2671
|
+
};
|
|
2672
|
+
}).sort((x2, y) => x2.id.localeCompare(y.id));
|
|
2673
|
+
}
|
|
2674
|
+
function getResource(dir, id) {
|
|
2675
|
+
const match = discoverKnowledge(dir).find(
|
|
2676
|
+
(a2) => a2.type === RESOURCE_ARTIFACT_TYPE && (a2.id === id || a2.title === id)
|
|
2677
|
+
);
|
|
2678
|
+
if (!match) return null;
|
|
2679
|
+
const raw = readFile(match.filePath);
|
|
2680
|
+
return toDetail(match.filePath, match.relPath, raw);
|
|
2681
|
+
}
|
|
2682
|
+
function parseWorkItemResources(fm) {
|
|
2683
|
+
const v = fm.resources;
|
|
2684
|
+
if (!Array.isArray(v)) return [];
|
|
2685
|
+
const out = [];
|
|
2686
|
+
for (const raw of v) {
|
|
2687
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
2688
|
+
const o2 = raw;
|
|
2689
|
+
const id = str(o2.id);
|
|
2690
|
+
if (!id) continue;
|
|
2691
|
+
out.push({ id, role: str(o2.role) ?? "affected" });
|
|
2692
|
+
}
|
|
2693
|
+
return out;
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2593
2696
|
// ../cli/src/core/graph.ts
|
|
2594
2697
|
var KNOWLEDGE3 = "knowledge";
|
|
2698
|
+
var RESOURCE_ROLE_EDGE = {
|
|
2699
|
+
affected: "affects",
|
|
2700
|
+
implementation: "uses",
|
|
2701
|
+
validation: "validates_with",
|
|
2702
|
+
delivery: "delivers_through"
|
|
2703
|
+
};
|
|
2595
2704
|
var ACTIVE_STATUSES = ["draft", "ready", "in-progress", "blocked"];
|
|
2596
2705
|
var ALL_STATUSES = ["draft", "ready", "in-progress", "blocked", "completed"];
|
|
2597
2706
|
function scopeStatuses(scope) {
|
|
@@ -2670,6 +2779,16 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2670
2779
|
if (cand.materializedAs) addEdge(candId, `wi:${cand.materializedAs}`, "materialized_as");
|
|
2671
2780
|
}
|
|
2672
2781
|
}
|
|
2782
|
+
const resourceTitleById = /* @__PURE__ */ new Map();
|
|
2783
|
+
for (const r2 of getResources(dir)) {
|
|
2784
|
+
resourceTitleById.set(r2.id, r2.title);
|
|
2785
|
+
addNode({
|
|
2786
|
+
id: `resource:${r2.id}`,
|
|
2787
|
+
type: "project-resource",
|
|
2788
|
+
label: r2.title ? `${r2.id} ${r2.title}`.trim() : r2.id,
|
|
2789
|
+
path: r2.path
|
|
2790
|
+
});
|
|
2791
|
+
}
|
|
2673
2792
|
for (const wi of selectedWIs) {
|
|
2674
2793
|
const id = wi.id || wi.title;
|
|
2675
2794
|
if (!id || !id.trim()) continue;
|
|
@@ -2694,6 +2813,12 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2694
2813
|
addNode({ id: capId, type: "capability", label: cap });
|
|
2695
2814
|
addEdge(wiNodeId, capId, "implements");
|
|
2696
2815
|
}
|
|
2816
|
+
for (const rel of parseWorkItemResources(wi.rawFrontmatter)) {
|
|
2817
|
+
const resId = `resource:${rel.id}`;
|
|
2818
|
+
const title = resourceTitleById.get(rel.id);
|
|
2819
|
+
addNode({ id: resId, type: "project-resource", label: title ? `${rel.id} ${title}`.trim() : rel.id });
|
|
2820
|
+
addEdge(wiNodeId, resId, RESOURCE_ROLE_EDGE[rel.role] ?? "uses");
|
|
2821
|
+
}
|
|
2697
2822
|
for (const dec of wi.decisions) {
|
|
2698
2823
|
if (!dec || !dec.trim()) continue;
|
|
2699
2824
|
const adrId = `adr:${dec}`;
|
|
@@ -3072,7 +3197,7 @@ function loadGraphHints(dir) {
|
|
|
3072
3197
|
}
|
|
3073
3198
|
|
|
3074
3199
|
// ../cli/src/services/installed-skills.ts
|
|
3075
|
-
import
|
|
3200
|
+
import matter7 from "gray-matter";
|
|
3076
3201
|
var SKILLS_DIR = join("knowledge", "skills");
|
|
3077
3202
|
function discoverInstalledSkills(dir) {
|
|
3078
3203
|
const base = join(dir, SKILLS_DIR);
|
|
@@ -3082,7 +3207,7 @@ function discoverInstalledSkills(dir) {
|
|
|
3082
3207
|
const skillFile = join(base, entry, "skill.md");
|
|
3083
3208
|
if (!isFile(skillFile)) continue;
|
|
3084
3209
|
try {
|
|
3085
|
-
const { data } =
|
|
3210
|
+
const { data } = matter7(readFile(skillFile));
|
|
3086
3211
|
if (data.type && String(data.type) !== "skill") continue;
|
|
3087
3212
|
const id = String(data.id ?? entry);
|
|
3088
3213
|
out.push({
|
|
@@ -4671,7 +4796,7 @@ function deriveDeliveryCompletedStatus(dir) {
|
|
|
4671
4796
|
}
|
|
4672
4797
|
|
|
4673
4798
|
// ../cli/src/core/assets.ts
|
|
4674
|
-
import
|
|
4799
|
+
import matter8 from "gray-matter";
|
|
4675
4800
|
|
|
4676
4801
|
// ../cli/src/core/version.ts
|
|
4677
4802
|
import { fileURLToPath } from "url";
|
|
@@ -4826,8 +4951,13 @@ dependencies.
|
|
|
4826
4951
|
flags, content/copy, authentication/authorization, notifications, analytics, documentation,
|
|
4827
4952
|
operations/release \u2014 as affected, reviewed-not-affected, unknown, or not-applicable.
|
|
4828
4953
|
4. **Module review** \u2014 for multirepo, evaluate each mapped module with the same statuses.
|
|
4829
|
-
5. **
|
|
4830
|
-
|
|
4954
|
+
5. **Resource review** \u2014 ask: does this change need to observe, modify, validate or deliver against a
|
|
4955
|
+
known external resource of the project (a Project Resource under \`knowledge/tech/resources/\` \u2014
|
|
4956
|
+
database, cloud, API, queue, storage\u2026)? If so, declare it in the WI front matter \`resources:\` with
|
|
4957
|
+
a role (\`affected\`, \`implementation\`, \`validation\`, \`delivery\`). Resources are external systems,
|
|
4958
|
+
distinct from \`affected_modules\` (code). Not every WI needs resources (e.g. a CSS change does not).
|
|
4959
|
+
6. **Completeness review** \u2014 confirm: outcome covered, journey covered, modules assessed, resources
|
|
4960
|
+
assessed, unknowns visible, acceptance criteria end-to-end, scope and out-of-scope coherent.
|
|
4831
4961
|
|
|
4832
4962
|
## Rules
|
|
4833
4963
|
|
|
@@ -4838,6 +4968,8 @@ dependencies.
|
|
|
4838
4968
|
- Include at least one end-to-end acceptance criterion for user-facing changes.
|
|
4839
4969
|
- Do not reduce a product intent to the first technical implementation found.
|
|
4840
4970
|
- Evaluate surfaces and modules before proposing files.
|
|
4971
|
+
- Consider Project Resources when the scope justifies it; never invent resources not backed by
|
|
4972
|
+
project knowledge, and keep \`resources\` (external systems) separate from \`affected_modules\` (code).
|
|
4841
4973
|
- This skill refines scope and acceptance criteria but does not approve implementation readiness.
|
|
4842
4974
|
Readiness requires human confirmation through \`kaddo ready\` or the MCP \`mark_work_item_ready\` action.
|
|
4843
4975
|
|
|
@@ -7877,7 +8009,7 @@ function canonicalSkills() {
|
|
|
7877
8009
|
}
|
|
7878
8010
|
function versionOf(content) {
|
|
7879
8011
|
try {
|
|
7880
|
-
const v =
|
|
8012
|
+
const v = matter8(content).data.version;
|
|
7881
8013
|
return v === void 0 || v === null ? null : String(v);
|
|
7882
8014
|
} catch {
|
|
7883
8015
|
return null;
|
|
@@ -8649,7 +8781,7 @@ function renderRouteCompact(route) {
|
|
|
8649
8781
|
}
|
|
8650
8782
|
|
|
8651
8783
|
// ../cli/src/core/metadata-health.ts
|
|
8652
|
-
import
|
|
8784
|
+
import matter9 from "gray-matter";
|
|
8653
8785
|
var KNOWLEDGE_FILES2 = [
|
|
8654
8786
|
"knowledge/business/business.md",
|
|
8655
8787
|
"knowledge/product/product.md",
|
|
@@ -8669,7 +8801,7 @@ function analyzeMetadataHealth(dir) {
|
|
|
8669
8801
|
let data;
|
|
8670
8802
|
try {
|
|
8671
8803
|
const raw = readFile(p);
|
|
8672
|
-
data =
|
|
8804
|
+
data = matter9(raw).data;
|
|
8673
8805
|
} catch {
|
|
8674
8806
|
continue;
|
|
8675
8807
|
}
|
|
@@ -8701,7 +8833,7 @@ function analyzeMetadataHealth(dir) {
|
|
|
8701
8833
|
if (isFile(p) && entry.endsWith(".md")) {
|
|
8702
8834
|
try {
|
|
8703
8835
|
const raw = readFile(p);
|
|
8704
|
-
const fm =
|
|
8836
|
+
const fm = matter9(raw).data;
|
|
8705
8837
|
if (fm.status === "done") {
|
|
8706
8838
|
const rel = p.replace(dir + "/", "").replace(dir + "\\", "").replace(/\\/g, "/");
|
|
8707
8839
|
findings.push({
|
|
@@ -9656,7 +9788,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
9656
9788
|
}
|
|
9657
9789
|
}
|
|
9658
9790
|
try {
|
|
9659
|
-
const body =
|
|
9791
|
+
const body = matter10(readFile(wi.filePath)).content;
|
|
9660
9792
|
if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
|
|
9661
9793
|
else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
|
|
9662
9794
|
if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
|
|
@@ -10369,7 +10501,7 @@ function serializeDriftJson(r2) {
|
|
|
10369
10501
|
}
|
|
10370
10502
|
|
|
10371
10503
|
// src/workitems.ts
|
|
10372
|
-
import
|
|
10504
|
+
import matter11 from "gray-matter";
|
|
10373
10505
|
var VALID_SOURCES2 = /* @__PURE__ */ new Set(["manual", "roadmap", "jira", "github", "notion", "xlsx", "csv", "api", "external", "unknown"]);
|
|
10374
10506
|
function optStr3(v) {
|
|
10375
10507
|
return typeof v === "string" && v.trim() ? v.trim() : void 0;
|
|
@@ -10392,7 +10524,7 @@ function parseSource(data) {
|
|
|
10392
10524
|
return { type: "unknown", inferred: true };
|
|
10393
10525
|
}
|
|
10394
10526
|
var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["draft", "ready", "in-progress", "blocked"]);
|
|
10395
|
-
function
|
|
10527
|
+
function strArray2(v) {
|
|
10396
10528
|
return Array.isArray(v) ? v.map((x2) => String(x2)).filter(Boolean) : [];
|
|
10397
10529
|
}
|
|
10398
10530
|
function lifecycleOf(data, relPath) {
|
|
@@ -10460,15 +10592,15 @@ function listWorkItems(root) {
|
|
|
10460
10592
|
if (raw === null) continue;
|
|
10461
10593
|
let parsed;
|
|
10462
10594
|
try {
|
|
10463
|
-
parsed =
|
|
10595
|
+
parsed = matter11(raw);
|
|
10464
10596
|
} catch {
|
|
10465
10597
|
continue;
|
|
10466
10598
|
}
|
|
10467
10599
|
const data = parsed.data;
|
|
10468
10600
|
if (!data.type) continue;
|
|
10469
10601
|
const status = lifecycleOf(data, rel);
|
|
10470
|
-
const code =
|
|
10471
|
-
const domains =
|
|
10602
|
+
const code = strArray2(data.code);
|
|
10603
|
+
const domains = strArray2(data.domains);
|
|
10472
10604
|
const source = parseSource(data);
|
|
10473
10605
|
const readiness = status === "draft" ? assessReadiness(data, parsed.content, code, domains, source) : void 0;
|
|
10474
10606
|
items.push({
|
|
@@ -10480,9 +10612,9 @@ function listWorkItems(root) {
|
|
|
10480
10612
|
path: rel,
|
|
10481
10613
|
summary: String(data.summary ?? "") || firstParagraph2(parsed.content),
|
|
10482
10614
|
code,
|
|
10483
|
-
capabilities:
|
|
10484
|
-
decisions:
|
|
10485
|
-
capsules:
|
|
10615
|
+
capabilities: strArray2(data.capabilities),
|
|
10616
|
+
decisions: strArray2(data.decisions),
|
|
10617
|
+
capsules: strArray2(data.capsules),
|
|
10486
10618
|
source,
|
|
10487
10619
|
domains,
|
|
10488
10620
|
readiness,
|
|
@@ -10928,14 +11060,14 @@ function getPrompt(root, name) {
|
|
|
10928
11060
|
}
|
|
10929
11061
|
|
|
10930
11062
|
// src/tools.ts
|
|
10931
|
-
import
|
|
11063
|
+
import matter13 from "gray-matter";
|
|
10932
11064
|
|
|
10933
11065
|
// ../cli/dist/core.js
|
|
10934
11066
|
import fs3 from "fs";
|
|
10935
11067
|
import path3 from "path";
|
|
10936
11068
|
import { z as z3 } from "zod";
|
|
10937
11069
|
import { parse as parseYaml10 } from "yaml";
|
|
10938
|
-
import
|
|
11070
|
+
import matter12 from "gray-matter";
|
|
10939
11071
|
import matter22 from "gray-matter";
|
|
10940
11072
|
import { parse as parseYaml62, stringify as stringifyYaml32 } from "yaml";
|
|
10941
11073
|
import { parse as parseYaml22 } from "yaml";
|
|
@@ -10944,25 +11076,26 @@ import { parse as parseYaml52, stringify as stringifyYaml22 } from "yaml";
|
|
|
10944
11076
|
import { parse as parseYaml42, stringify as stringifyYaml4 } from "yaml";
|
|
10945
11077
|
import matter32 from "gray-matter";
|
|
10946
11078
|
import matter42 from "gray-matter";
|
|
10947
|
-
import { parse as parseYaml72 } from "yaml";
|
|
10948
11079
|
import matter52 from "gray-matter";
|
|
11080
|
+
import { parse as parseYaml72 } from "yaml";
|
|
11081
|
+
import matter62 from "gray-matter";
|
|
10949
11082
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10950
11083
|
import { dirname as dirname2, join as join22, parse as parse2 } from "path";
|
|
10951
11084
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
10952
|
-
import matter62 from "gray-matter";
|
|
10953
11085
|
import matter72 from "gray-matter";
|
|
11086
|
+
import matter82 from "gray-matter";
|
|
10954
11087
|
import { parse as parseYaml82, stringify as stringifyYaml42 } from "yaml";
|
|
10955
11088
|
import fs32 from "fs";
|
|
10956
11089
|
import path32 from "path";
|
|
10957
11090
|
import crypto2 from "crypto";
|
|
10958
|
-
import
|
|
11091
|
+
import matter92 from "gray-matter";
|
|
10959
11092
|
import crypto3 from "crypto";
|
|
10960
11093
|
import fs4 from "fs";
|
|
10961
|
-
import matter92 from "gray-matter";
|
|
10962
11094
|
import matter102 from "gray-matter";
|
|
11095
|
+
import matter112 from "gray-matter";
|
|
10963
11096
|
import { parse as parseYaml92, stringify as stringifyYaml5 } from "yaml";
|
|
10964
11097
|
import { parse as parseYaml102, stringify as stringifyYaml6 } from "yaml";
|
|
10965
|
-
import
|
|
11098
|
+
import matter122 from "gray-matter";
|
|
10966
11099
|
function exists2(p) {
|
|
10967
11100
|
return fs3.existsSync(p);
|
|
10968
11101
|
}
|
|
@@ -11130,7 +11263,7 @@ function parseImpactAnalysis2(data) {
|
|
|
11130
11263
|
}
|
|
11131
11264
|
function parseArtifact2(filePath, raw) {
|
|
11132
11265
|
try {
|
|
11133
|
-
const { data } =
|
|
11266
|
+
const { data } = matter12(raw);
|
|
11134
11267
|
const globs = Array.isArray(data.code) ? data.code.filter(Boolean) : [];
|
|
11135
11268
|
return {
|
|
11136
11269
|
filePath,
|
|
@@ -12684,7 +12817,113 @@ function suggestInitiativesForExternalItem(dir, integration, externalId) {
|
|
|
12684
12817
|
)
|
|
12685
12818
|
);
|
|
12686
12819
|
}
|
|
12820
|
+
var RESOURCE_ARTIFACT_TYPE2 = "project-resource";
|
|
12821
|
+
function str2(v) {
|
|
12822
|
+
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
12823
|
+
}
|
|
12824
|
+
function strArray3(v) {
|
|
12825
|
+
if (Array.isArray(v)) return v.map((x2) => String(x2).trim()).filter(Boolean);
|
|
12826
|
+
if (typeof v === "string" && v.trim()) return [v.trim()];
|
|
12827
|
+
return [];
|
|
12828
|
+
}
|
|
12829
|
+
function parseInterfaces2(v) {
|
|
12830
|
+
if (!Array.isArray(v)) return [];
|
|
12831
|
+
const out = [];
|
|
12832
|
+
for (const raw of v) {
|
|
12833
|
+
if (!raw || typeof raw !== "object") continue;
|
|
12834
|
+
const o2 = raw;
|
|
12835
|
+
const type = str2(o2.type);
|
|
12836
|
+
if (!type) continue;
|
|
12837
|
+
out.push({
|
|
12838
|
+
type,
|
|
12839
|
+
tool: str2(o2.tool),
|
|
12840
|
+
provider: str2(o2.provider),
|
|
12841
|
+
purpose: str2(o2.purpose),
|
|
12842
|
+
operations: strArray3(o2.operations),
|
|
12843
|
+
environments: strArray3(o2.environments),
|
|
12844
|
+
constraints: str2(o2.constraints)
|
|
12845
|
+
});
|
|
12846
|
+
}
|
|
12847
|
+
return out;
|
|
12848
|
+
}
|
|
12849
|
+
function parseBoundaries2(v) {
|
|
12850
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
12851
|
+
const out = {};
|
|
12852
|
+
for (const [env, val] of Object.entries(v)) {
|
|
12853
|
+
const list22 = strArray3(val);
|
|
12854
|
+
if (list22.length > 0) out[env] = list22;
|
|
12855
|
+
}
|
|
12856
|
+
return out;
|
|
12857
|
+
}
|
|
12858
|
+
function parseAuth2(v) {
|
|
12859
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return { mode: null, refs: [] };
|
|
12860
|
+
const o2 = v;
|
|
12861
|
+
const refs = strArray3(o2.refs).filter((r2) => !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
|
|
12862
|
+
return { mode: str2(o2.mode), refs };
|
|
12863
|
+
}
|
|
12864
|
+
function toDetail2(filePath, relPath, raw) {
|
|
12865
|
+
const parsed = matter42(raw);
|
|
12866
|
+
const fm = parsed.data ?? {};
|
|
12867
|
+
const body = parsed.content.trim();
|
|
12868
|
+
const id = str2(fm.id) ?? "";
|
|
12869
|
+
return {
|
|
12870
|
+
id,
|
|
12871
|
+
title: str2(fm.title) ?? id,
|
|
12872
|
+
resourceType: str2(fm.resource_type),
|
|
12873
|
+
provider: str2(fm.provider),
|
|
12874
|
+
environments: strArray3(fm.environments),
|
|
12875
|
+
path: relPath,
|
|
12876
|
+
purpose: str2(fm.purpose),
|
|
12877
|
+
interfaces: parseInterfaces2(fm.access_interfaces),
|
|
12878
|
+
boundaries: parseBoundaries2(fm.access_boundaries),
|
|
12879
|
+
...(() => {
|
|
12880
|
+
const a2 = parseAuth2(fm.authentication);
|
|
12881
|
+
return { authRefs: a2.refs, authMode: a2.mode };
|
|
12882
|
+
})(),
|
|
12883
|
+
markdownBody: body
|
|
12884
|
+
};
|
|
12885
|
+
}
|
|
12886
|
+
function getResources2(dir) {
|
|
12887
|
+
return discoverKnowledge2(dir).filter((a2) => a2.type === RESOURCE_ARTIFACT_TYPE2).map((a2) => {
|
|
12888
|
+
const fm = a2.rawFrontmatter;
|
|
12889
|
+
return {
|
|
12890
|
+
id: a2.id || str2(fm.id) || a2.title,
|
|
12891
|
+
title: a2.title || str2(fm.title) || a2.id,
|
|
12892
|
+
resourceType: str2(fm.resource_type),
|
|
12893
|
+
provider: str2(fm.provider),
|
|
12894
|
+
environments: strArray3(fm.environments),
|
|
12895
|
+
path: a2.relPath
|
|
12896
|
+
};
|
|
12897
|
+
}).sort((x2, y) => x2.id.localeCompare(y.id));
|
|
12898
|
+
}
|
|
12899
|
+
function getResource2(dir, id) {
|
|
12900
|
+
const match = discoverKnowledge2(dir).find(
|
|
12901
|
+
(a2) => a2.type === RESOURCE_ARTIFACT_TYPE2 && (a2.id === id || a2.title === id)
|
|
12902
|
+
);
|
|
12903
|
+
if (!match) return null;
|
|
12904
|
+
const raw = readFile2(match.filePath);
|
|
12905
|
+
return toDetail2(match.filePath, match.relPath, raw);
|
|
12906
|
+
}
|
|
12907
|
+
function parseWorkItemResources2(fm) {
|
|
12908
|
+
const v = fm.resources;
|
|
12909
|
+
if (!Array.isArray(v)) return [];
|
|
12910
|
+
const out = [];
|
|
12911
|
+
for (const raw of v) {
|
|
12912
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
12913
|
+
const o2 = raw;
|
|
12914
|
+
const id = str2(o2.id);
|
|
12915
|
+
if (!id) continue;
|
|
12916
|
+
out.push({ id, role: str2(o2.role) ?? "affected" });
|
|
12917
|
+
}
|
|
12918
|
+
return out;
|
|
12919
|
+
}
|
|
12687
12920
|
var KNOWLEDGE22 = "knowledge";
|
|
12921
|
+
var RESOURCE_ROLE_EDGE2 = {
|
|
12922
|
+
affected: "affects",
|
|
12923
|
+
implementation: "uses",
|
|
12924
|
+
validation: "validates_with",
|
|
12925
|
+
delivery: "delivers_through"
|
|
12926
|
+
};
|
|
12688
12927
|
var ACTIVE_STATUSES2 = ["draft", "ready", "in-progress", "blocked"];
|
|
12689
12928
|
var ALL_STATUSES2 = ["draft", "ready", "in-progress", "blocked", "completed"];
|
|
12690
12929
|
function scopeStatuses2(scope) {
|
|
@@ -12763,6 +13002,16 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
12763
13002
|
if (cand.materializedAs) addEdge(candId, `wi:${cand.materializedAs}`, "materialized_as");
|
|
12764
13003
|
}
|
|
12765
13004
|
}
|
|
13005
|
+
const resourceTitleById = /* @__PURE__ */ new Map();
|
|
13006
|
+
for (const r2 of getResources2(dir)) {
|
|
13007
|
+
resourceTitleById.set(r2.id, r2.title);
|
|
13008
|
+
addNode({
|
|
13009
|
+
id: `resource:${r2.id}`,
|
|
13010
|
+
type: "project-resource",
|
|
13011
|
+
label: r2.title ? `${r2.id} ${r2.title}`.trim() : r2.id,
|
|
13012
|
+
path: r2.path
|
|
13013
|
+
});
|
|
13014
|
+
}
|
|
12766
13015
|
for (const wi of selectedWIs) {
|
|
12767
13016
|
const id = wi.id || wi.title;
|
|
12768
13017
|
if (!id || !id.trim()) continue;
|
|
@@ -12787,6 +13036,12 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
12787
13036
|
addNode({ id: capId, type: "capability", label: cap });
|
|
12788
13037
|
addEdge(wiNodeId, capId, "implements");
|
|
12789
13038
|
}
|
|
13039
|
+
for (const rel of parseWorkItemResources2(wi.rawFrontmatter)) {
|
|
13040
|
+
const resId = `resource:${rel.id}`;
|
|
13041
|
+
const title = resourceTitleById.get(rel.id);
|
|
13042
|
+
addNode({ id: resId, type: "project-resource", label: title ? `${rel.id} ${title}`.trim() : rel.id });
|
|
13043
|
+
addEdge(wiNodeId, resId, RESOURCE_ROLE_EDGE2[rel.role] ?? "uses");
|
|
13044
|
+
}
|
|
12790
13045
|
for (const dec of wi.decisions) {
|
|
12791
13046
|
if (!dec || !dec.trim()) continue;
|
|
12792
13047
|
const adrId = `adr:${dec}`;
|
|
@@ -13280,8 +13535,13 @@ dependencies.
|
|
|
13280
13535
|
flags, content/copy, authentication/authorization, notifications, analytics, documentation,
|
|
13281
13536
|
operations/release \u2014 as affected, reviewed-not-affected, unknown, or not-applicable.
|
|
13282
13537
|
4. **Module review** \u2014 for multirepo, evaluate each mapped module with the same statuses.
|
|
13283
|
-
5. **
|
|
13284
|
-
|
|
13538
|
+
5. **Resource review** \u2014 ask: does this change need to observe, modify, validate or deliver against a
|
|
13539
|
+
known external resource of the project (a Project Resource under \`knowledge/tech/resources/\` \u2014
|
|
13540
|
+
database, cloud, API, queue, storage\u2026)? If so, declare it in the WI front matter \`resources:\` with
|
|
13541
|
+
a role (\`affected\`, \`implementation\`, \`validation\`, \`delivery\`). Resources are external systems,
|
|
13542
|
+
distinct from \`affected_modules\` (code). Not every WI needs resources (e.g. a CSS change does not).
|
|
13543
|
+
6. **Completeness review** \u2014 confirm: outcome covered, journey covered, modules assessed, resources
|
|
13544
|
+
assessed, unknowns visible, acceptance criteria end-to-end, scope and out-of-scope coherent.
|
|
13285
13545
|
|
|
13286
13546
|
## Rules
|
|
13287
13547
|
|
|
@@ -13292,6 +13552,8 @@ dependencies.
|
|
|
13292
13552
|
- Include at least one end-to-end acceptance criterion for user-facing changes.
|
|
13293
13553
|
- Do not reduce a product intent to the first technical implementation found.
|
|
13294
13554
|
- Evaluate surfaces and modules before proposing files.
|
|
13555
|
+
- Consider Project Resources when the scope justifies it; never invent resources not backed by
|
|
13556
|
+
project knowledge, and keep \`resources\` (external systems) separate from \`affected_modules\` (code).
|
|
13295
13557
|
- This skill refines scope and acceptance criteria but does not approve implementation readiness.
|
|
13296
13558
|
Readiness requires human confirmation through \`kaddo ready\` or the MCP \`mark_work_item_ready\` action.
|
|
13297
13559
|
|
|
@@ -16979,6 +17241,8 @@ function getWorkItem(dir, workItemId) {
|
|
|
16979
17241
|
path: match.relPath,
|
|
16980
17242
|
// Faithful source of truth: the artifact body already read above, not reconstructed (WI-025).
|
|
16981
17243
|
markdownBody: body.trim(),
|
|
17244
|
+
// External systems this WI relates to (WI-030), separate from affectedModules (code).
|
|
17245
|
+
resources: parseWorkItemResources2(fm),
|
|
16982
17246
|
...parseSystemImpact(dir, fm)
|
|
16983
17247
|
};
|
|
16984
17248
|
return { ...detail, refinement: computeRefinementStatus(detail) };
|
|
@@ -17043,7 +17307,7 @@ function parseOriginalSnapshot(fm) {
|
|
|
17043
17307
|
function readBody(filePath) {
|
|
17044
17308
|
try {
|
|
17045
17309
|
const raw = readFile2(filePath);
|
|
17046
|
-
return
|
|
17310
|
+
return matter82(raw).content;
|
|
17047
17311
|
} catch {
|
|
17048
17312
|
return "";
|
|
17049
17313
|
}
|
|
@@ -17328,7 +17592,7 @@ function ensureH1(body, title) {
|
|
|
17328
17592
|
${trimmed}`;
|
|
17329
17593
|
}
|
|
17330
17594
|
function serialize(data, body) {
|
|
17331
|
-
return
|
|
17595
|
+
return matter92.stringify(`
|
|
17332
17596
|
${body.trim()}
|
|
17333
17597
|
`, data);
|
|
17334
17598
|
}
|
|
@@ -17722,6 +17986,7 @@ function buildImplementationHandoff(dir, workItemId) {
|
|
|
17722
17986
|
const affectedModules = match.affectedModules;
|
|
17723
17987
|
const domains = match.domains;
|
|
17724
17988
|
const scopeConfidence = match.scopeConfidence;
|
|
17989
|
+
const wiResources = parseWorkItemResources2(match.rawFrontmatter);
|
|
17725
17990
|
const lines = [
|
|
17726
17991
|
`Implement Work Item ${wiId} \u2014 "${wiTitle}" \u2014 in project "${projectName}" using Kaddo.`,
|
|
17727
17992
|
"",
|
|
@@ -17811,6 +18076,41 @@ function buildImplementationHandoff(dir, workItemId) {
|
|
|
17811
18076
|
"Do not copy all legacy knowledge \u2014 assemble proportionally to scope."
|
|
17812
18077
|
);
|
|
17813
18078
|
}
|
|
18079
|
+
if (wiResources.length > 0) {
|
|
18080
|
+
lines.push(
|
|
18081
|
+
"",
|
|
18082
|
+
"--- Relevant Resources ---",
|
|
18083
|
+
"",
|
|
18084
|
+
"This Work Item relates to external project resources. The handoff INFORMS you about them \u2014 it",
|
|
18085
|
+
"never connects to them, runs any interface, or reveals credential values. Respect each boundary."
|
|
18086
|
+
);
|
|
18087
|
+
for (const rel of wiResources) {
|
|
18088
|
+
const res = getResource2(dir, rel.id);
|
|
18089
|
+
if (!res) {
|
|
18090
|
+
lines.push("", `- ${rel.id} (role: ${rel.role}) \u2014 definition not found under knowledge/tech/resources/.`);
|
|
18091
|
+
continue;
|
|
18092
|
+
}
|
|
18093
|
+
lines.push("", `- ${res.id} \u2014 ${res.title} (role: ${rel.role})`);
|
|
18094
|
+
if (res.resourceType || res.provider) {
|
|
18095
|
+
lines.push(` type: ${res.resourceType ?? "\u2014"}${res.provider ? ` \xB7 provider: ${res.provider}` : ""}`);
|
|
18096
|
+
}
|
|
18097
|
+
if (res.purpose) lines.push(` purpose: ${res.purpose}`);
|
|
18098
|
+
if (res.environments.length > 0) lines.push(` environments: ${res.environments.join(", ")}`);
|
|
18099
|
+
if (res.interfaces.length > 0) {
|
|
18100
|
+
const ifaces = res.interfaces.map((i) => i.tool || i.provider ? `${i.type} (${i.tool ?? i.provider})` : i.type);
|
|
18101
|
+
lines.push(` interfaces: ${ifaces.join(", ")}`);
|
|
18102
|
+
}
|
|
18103
|
+
for (const [env, ops] of Object.entries(res.boundaries)) {
|
|
18104
|
+
lines.push(` boundary (${env}): ${ops.join(", ")}`);
|
|
18105
|
+
}
|
|
18106
|
+
if (res.authRefs.length > 0) lines.push(` auth references (names only): ${res.authRefs.join(", ")}`);
|
|
18107
|
+
}
|
|
18108
|
+
lines.push(
|
|
18109
|
+
"",
|
|
18110
|
+
"Treat these as knowledge, not availability: an interface being declared does not mean it is",
|
|
18111
|
+
"installed or connected. Do not execute any interface or migration without explicit human confirmation."
|
|
18112
|
+
);
|
|
18113
|
+
}
|
|
17814
18114
|
lines.push(
|
|
17815
18115
|
"",
|
|
17816
18116
|
"--- Implementation Plan ---",
|
|
@@ -17831,6 +18131,7 @@ function buildImplementationHandoff(dir, workItemId) {
|
|
|
17831
18131
|
projectName,
|
|
17832
18132
|
lifecycle,
|
|
17833
18133
|
affectedModules,
|
|
18134
|
+
resources: wiResources,
|
|
17834
18135
|
domains,
|
|
17835
18136
|
scopeConfidence,
|
|
17836
18137
|
hasUnmaterializedDecisions: unmaterialized,
|
|
@@ -18082,7 +18383,7 @@ function detectFormat(raw) {
|
|
|
18082
18383
|
if (!trimmed) throw new ImportError("EMPTY_INPUT", "Import content is empty.");
|
|
18083
18384
|
if (trimmed.startsWith("---")) {
|
|
18084
18385
|
try {
|
|
18085
|
-
const { data } =
|
|
18386
|
+
const { data } = matter102(trimmed);
|
|
18086
18387
|
if (data && typeof data === "object" && Object.keys(data).length > 0) {
|
|
18087
18388
|
const d3 = data;
|
|
18088
18389
|
if (d3.type === "work-item" || d3.id && typeof d3.id === "string" && /^WI-\d+/.test(d3.id)) {
|
|
@@ -18104,7 +18405,7 @@ function parseContent(raw) {
|
|
|
18104
18405
|
let bodyText = trimmed;
|
|
18105
18406
|
if (format === "kaddo-frontmatter" || format === "markdown-frontmatter") {
|
|
18106
18407
|
try {
|
|
18107
|
-
const parsed =
|
|
18408
|
+
const parsed = matter102(trimmed);
|
|
18108
18409
|
frontmatterFields = parsed.data ?? {};
|
|
18109
18410
|
bodyText = parsed.content.trim();
|
|
18110
18411
|
} catch {
|
|
@@ -18201,7 +18502,7 @@ function findDuplicateByHash(dir, hash) {
|
|
|
18201
18502
|
const raw = readFile2(item.filePath);
|
|
18202
18503
|
if (!raw) continue;
|
|
18203
18504
|
try {
|
|
18204
|
-
const { data } =
|
|
18505
|
+
const { data } = matter102(raw);
|
|
18205
18506
|
const source = data?.source;
|
|
18206
18507
|
if (source && typeof source === "object" && !Array.isArray(source)) {
|
|
18207
18508
|
const s = source;
|
|
@@ -19096,7 +19397,7 @@ function buildInitiativeContent(id, input) {
|
|
|
19096
19397
|
if (input.priority) data.priority = input.priority;
|
|
19097
19398
|
if (input.source) data.source = input.source;
|
|
19098
19399
|
if (input.sourceId) data.source_id = input.sourceId;
|
|
19099
|
-
return
|
|
19400
|
+
return matter122.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
|
|
19100
19401
|
}
|
|
19101
19402
|
function createInitiative(dir, input) {
|
|
19102
19403
|
if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
|
|
@@ -19120,10 +19421,10 @@ function transitionInitiative(dir, id, to) {
|
|
|
19120
19421
|
throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
|
|
19121
19422
|
}
|
|
19122
19423
|
const raw = readFile2(ini.filePath);
|
|
19123
|
-
const { data, content } =
|
|
19424
|
+
const { data, content } = matter122(raw, {});
|
|
19124
19425
|
data.status = to;
|
|
19125
19426
|
if (to === "completed") data.completed_at = today2();
|
|
19126
|
-
const newContent =
|
|
19427
|
+
const newContent = matter122.stringify(content, data);
|
|
19127
19428
|
writeFile2(ini.filePath, newContent);
|
|
19128
19429
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19129
19430
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
|
|
@@ -19133,7 +19434,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
19133
19434
|
const ini = getInitiative(dir, id);
|
|
19134
19435
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
19135
19436
|
const raw = readFile2(ini.filePath);
|
|
19136
|
-
const { data, content } =
|
|
19437
|
+
const { data, content } = matter122(raw, {});
|
|
19137
19438
|
const existing = Array.isArray(data.candidates) ? data.candidates : [];
|
|
19138
19439
|
const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
|
|
19139
19440
|
const normalized = normalizeCandidate(candidate, fallbackId);
|
|
@@ -19141,7 +19442,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
19141
19442
|
throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
|
|
19142
19443
|
}
|
|
19143
19444
|
data.candidates = [...existing, candidateToData(normalized)];
|
|
19144
|
-
const newContent =
|
|
19445
|
+
const newContent = matter122.stringify(content, data);
|
|
19145
19446
|
writeFile2(ini.filePath, newContent);
|
|
19146
19447
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19147
19448
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
|
|
@@ -19151,7 +19452,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
19151
19452
|
const ini = getInitiative(dir, id);
|
|
19152
19453
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
19153
19454
|
const raw = readFile2(ini.filePath);
|
|
19154
|
-
const { data, content } =
|
|
19455
|
+
const { data, content } = matter122(raw, {});
|
|
19155
19456
|
if (patch.title !== void 0) {
|
|
19156
19457
|
if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
|
|
19157
19458
|
data.title = patch.title.trim();
|
|
@@ -19161,7 +19462,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
19161
19462
|
if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
|
|
19162
19463
|
if (patch.domains !== void 0) data.domains = patch.domains;
|
|
19163
19464
|
if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
|
|
19164
|
-
const newContent =
|
|
19465
|
+
const newContent = matter122.stringify(content, data);
|
|
19165
19466
|
writeFile2(ini.filePath, newContent);
|
|
19166
19467
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19167
19468
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
|
|
@@ -19179,10 +19480,10 @@ function addExternalLink(dir, id, link) {
|
|
|
19179
19480
|
throw new InitiativeWriteError(`External link ${link.integration}:${link.externalId} already exists on ${id}.`);
|
|
19180
19481
|
}
|
|
19181
19482
|
const raw = readFile2(ini.filePath);
|
|
19182
|
-
const { data, content } =
|
|
19483
|
+
const { data, content } = matter122(raw, {});
|
|
19183
19484
|
const existing = Array.isArray(data.external_links) ? data.external_links : [];
|
|
19184
19485
|
data.external_links = [...existing, externalLinkToData(link)];
|
|
19185
|
-
const newContent =
|
|
19486
|
+
const newContent = matter122.stringify(content, data);
|
|
19186
19487
|
writeFile2(ini.filePath, newContent);
|
|
19187
19488
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19188
19489
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding an external link.");
|
|
@@ -19251,7 +19552,7 @@ function buildWorkItemContent(wiId, iniId, candidate) {
|
|
|
19251
19552
|
"_What did we learn? Update after completion._",
|
|
19252
19553
|
""
|
|
19253
19554
|
].join("\n");
|
|
19254
|
-
return
|
|
19555
|
+
return matter122.stringify("\n" + body, data);
|
|
19255
19556
|
}
|
|
19256
19557
|
function materializeCandidate(dir, id, candidateId) {
|
|
19257
19558
|
const ini = getInitiative(dir, id);
|
|
@@ -19268,13 +19569,13 @@ function materializeCandidate(dir, id, candidateId) {
|
|
|
19268
19569
|
ensureDir2(join3(dir, DRAFT_DIR));
|
|
19269
19570
|
writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
|
|
19270
19571
|
const raw = readFile2(ini.filePath);
|
|
19271
|
-
const { data, content } =
|
|
19572
|
+
const { data, content } = matter122(raw, {});
|
|
19272
19573
|
const cands = Array.isArray(data.candidates) ? data.candidates : [];
|
|
19273
19574
|
for (const c of cands) {
|
|
19274
19575
|
if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
|
|
19275
19576
|
}
|
|
19276
19577
|
data.candidates = cands;
|
|
19277
|
-
const newContent =
|
|
19578
|
+
const newContent = matter122.stringify(content, data);
|
|
19278
19579
|
writeFile2(ini.filePath, newContent);
|
|
19279
19580
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19280
19581
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
|
|
@@ -19480,12 +19781,12 @@ function markWorkItemReady(root, id, confirm2) {
|
|
|
19480
19781
|
}
|
|
19481
19782
|
const raw = readText(root, item.path);
|
|
19482
19783
|
if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
|
|
19483
|
-
const parsed =
|
|
19784
|
+
const parsed = matter13(raw);
|
|
19484
19785
|
const data = { ...parsed.data };
|
|
19485
19786
|
const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
19486
19787
|
data.status = "ready";
|
|
19487
19788
|
data.ready_at = today3;
|
|
19488
|
-
const newContent =
|
|
19789
|
+
const newContent = matter13.stringify(parsed.content, data);
|
|
19489
19790
|
const posixPath = item.path.replace(/\\/g, "/");
|
|
19490
19791
|
let newRelPath = item.path;
|
|
19491
19792
|
if (posixPath.includes("/work-items/draft/")) {
|
|
@@ -19648,11 +19949,19 @@ function setTelemetryConsentTool(root, args) {
|
|
|
19648
19949
|
persistConsent(root, consent);
|
|
19649
19950
|
return ok({ applied: true, consent, message: `Telemetry ${consent}.` });
|
|
19650
19951
|
}
|
|
19952
|
+
function listResourcesTool(root) {
|
|
19953
|
+
return ok(getResources(root));
|
|
19954
|
+
}
|
|
19955
|
+
function getResourceTool(root, id) {
|
|
19956
|
+
const resource = getResource(root, id);
|
|
19957
|
+
if (!resource) return fail(`Project Resource "${id}" not found.`);
|
|
19958
|
+
return ok(resource);
|
|
19959
|
+
}
|
|
19651
19960
|
|
|
19652
19961
|
// src/multirepo.ts
|
|
19653
19962
|
import fs5 from "fs";
|
|
19654
19963
|
import path4 from "path";
|
|
19655
|
-
import
|
|
19964
|
+
import matter14 from "gray-matter";
|
|
19656
19965
|
import { parse as parseYaml11, stringify as stringifyYaml7 } from "yaml";
|
|
19657
19966
|
var ok2 = (data) => ({ ok: true, data });
|
|
19658
19967
|
var fail2 = (message) => ({ ok: false, message });
|
|
@@ -19804,7 +20113,7 @@ function validateWorkItemModulesTool(root, args) {
|
|
|
19804
20113
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
19805
20114
|
const raw = readText(root, wi.path);
|
|
19806
20115
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
19807
|
-
const parsed =
|
|
20116
|
+
const parsed = matter14(raw);
|
|
19808
20117
|
const data = parsed.data;
|
|
19809
20118
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
19810
20119
|
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) : [] })) : [];
|
|
@@ -19857,7 +20166,7 @@ function getWorkItemContextTool(root, args) {
|
|
|
19857
20166
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
19858
20167
|
const raw = readText(root, wi.path);
|
|
19859
20168
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
19860
|
-
const parsed =
|
|
20169
|
+
const parsed = matter14(raw);
|
|
19861
20170
|
const data = parsed.data;
|
|
19862
20171
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
19863
20172
|
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) : [] })) : [];
|
|
@@ -19943,7 +20252,7 @@ function suggestBranchStrategyTool(root, args) {
|
|
|
19943
20252
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
19944
20253
|
const raw = readText(root, wi.path);
|
|
19945
20254
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
19946
|
-
const parsed =
|
|
20255
|
+
const parsed = matter14(raw);
|
|
19947
20256
|
const data = parsed.data;
|
|
19948
20257
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
19949
20258
|
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,14 +21012,14 @@ function systemImpactCandidatesTool(root, args) {
|
|
|
20703
21012
|
}
|
|
20704
21013
|
|
|
20705
21014
|
// ../cli/src/services/integrations.ts
|
|
20706
|
-
import
|
|
21015
|
+
import matter16 from "gray-matter";
|
|
20707
21016
|
import { parse as parseYaml13, stringify as stringifyYaml9 } from "yaml";
|
|
20708
21017
|
|
|
20709
21018
|
// ../cli/src/core/work-item-write.ts
|
|
20710
21019
|
import fs7 from "fs";
|
|
20711
21020
|
import path6 from "path";
|
|
20712
21021
|
import crypto4 from "crypto";
|
|
20713
|
-
import
|
|
21022
|
+
import matter15 from "gray-matter";
|
|
20714
21023
|
var SECTION_ORDER2 = [
|
|
20715
21024
|
"Actor",
|
|
20716
21025
|
"Outcome",
|
|
@@ -21758,7 +22067,7 @@ async function integrationsWorkItemTool(root, args) {
|
|
|
21758
22067
|
}
|
|
21759
22068
|
|
|
21760
22069
|
// ../cli/src/core/context-pack.ts
|
|
21761
|
-
import
|
|
22070
|
+
import matter17 from "gray-matter";
|
|
21762
22071
|
var CONTEXT_PACK_VERSION = "1";
|
|
21763
22072
|
var ARCH_DIR4 = "knowledge";
|
|
21764
22073
|
function readScanJson(dir) {
|
|
@@ -21771,7 +22080,7 @@ function readScanJson(dir) {
|
|
|
21771
22080
|
}
|
|
21772
22081
|
}
|
|
21773
22082
|
function firstParagraph4(markdown) {
|
|
21774
|
-
const body =
|
|
22083
|
+
const body = matter17(markdown).content.trim();
|
|
21775
22084
|
const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
|
|
21776
22085
|
return para ?? "";
|
|
21777
22086
|
}
|
|
@@ -21825,6 +22134,8 @@ function toContextWorkItem(a2) {
|
|
|
21825
22134
|
wi.validationStatus = a2.validationStatus || (isHistorical ? "not-assessed" : void 0);
|
|
21826
22135
|
wi.releaseStatus = a2.releaseStatus || (isHistorical ? "not-assessed" : void 0);
|
|
21827
22136
|
if (a2.affectedModules.length > 0) wi.affectedModules = a2.affectedModules;
|
|
22137
|
+
const wiResources = parseWorkItemResources(a2.rawFrontmatter);
|
|
22138
|
+
if (wiResources.length > 0) wi.resources = wiResources;
|
|
21828
22139
|
if (a2.scopeConfidence) wi.scopeConfidence = a2.scopeConfidence;
|
|
21829
22140
|
if (a2.moduleCoverage) wi.moduleCoverage = a2.moduleCoverage;
|
|
21830
22141
|
if (a2.impactAnalysis) wi.impactAnalysis = a2.impactAnalysis;
|
|
@@ -23157,6 +23468,16 @@ function createServer(root) {
|
|
|
23157
23468
|
},
|
|
23158
23469
|
async (args) => toolText(guarded(root, () => markWorkItemReady(root, args.id, args.confirm)))
|
|
23159
23470
|
);
|
|
23471
|
+
server.registerTool(
|
|
23472
|
+
"kaddo_list_resources",
|
|
23473
|
+
{ title: "List Project Resources", description: "List external systems the project uses (databases, cloud, APIs\u2026), read-only, no secrets.", inputSchema: {} },
|
|
23474
|
+
async () => toolText(guarded(root, () => listResourcesTool(root)))
|
|
23475
|
+
);
|
|
23476
|
+
server.registerTool(
|
|
23477
|
+
"kaddo_get_resource",
|
|
23478
|
+
{ title: "Get Project Resource", description: "Get one Project Resource by ID: purpose, environments, access interfaces, boundaries and auth references (never values).", inputSchema: { id: z4.string() } },
|
|
23479
|
+
async (args) => toolText(guarded(root, () => getResourceTool(root, args.id)))
|
|
23480
|
+
);
|
|
23160
23481
|
server.registerTool(
|
|
23161
23482
|
"kaddo_list_capsules",
|
|
23162
23483
|
{ title: "List Knowledge Capsules", description: "List registered external Knowledge Capsules.", inputSchema: {} },
|
package/package.json
CHANGED