@kaddo/cli 3.70.1 → 3.72.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.
@@ -0,0 +1,2 @@
1
+ /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.collapse{visibility:collapse}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.border{border-style:var(--tw-border-style);border-width:1px}.font-mono{font-family:var(--font-mono)}.break-all{word-break:break-all}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}}:root{--background:#fff;--surface:#f8f9fa;--surface-muted:#f1f3f5;--foreground:#1a1a2e;--foreground-muted:#6c757d;--border:#dee2e6;--border-strong:#adb5bd;--primary:#4361ee;--primary-foreground:#fff;--success:#2d9f5c;--warning:#e9a820;--danger:#dc3545;--info:#3b82f6;--finding-blocking:#dc3545;--finding-warning:#e9a820;--finding-fyi:#6c757d;--work-item-draft:#6c757d;--work-item-ready:#3b82f6;--work-item-progress:#8b5cf6;--work-item-blocked:#dc3545;--work-item-completed:#2d9f5c;--work-item-archived:#adb5bd;--knowledge-ready:#2d9f5c;--knowledge-missing:#dc3545;--knowledge-placeholder:#e9a820;--knowledge-unknown:#6c757d;--module-core:#4361ee;--module-module:#3b82f6;--module-unavailable:#dc3545;--readiness-ready:#2d9f5c;--readiness-warning:#e9a820;--readiness-blocked:#dc3545;--font-sans:"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono:"JetBrains Mono", "Fira Code", "Cascadia Code", monospace;--radius:6px}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--background:#0f0f23;--surface:#1a1a2e;--surface-muted:#16213e;--foreground:#e8e8e8;--foreground-muted:#a0a0b0;--border:#2a2a3e;--border-strong:#4a4a5e;--primary:#6580f5;--primary-foreground:#fff;--success:#3cb371;--warning:#f0b840;--danger:#ef4444;--info:#60a5fa;--finding-blocking:#ef4444;--finding-warning:#f0b840;--finding-fyi:#a0a0b0;--work-item-draft:#a0a0b0;--work-item-ready:#60a5fa;--work-item-progress:#a78bfa;--work-item-blocked:#ef4444;--work-item-completed:#3cb371;--work-item-archived:#6a6a7e;--knowledge-ready:#3cb371;--knowledge-missing:#ef4444;--knowledge-placeholder:#f0b840;--knowledge-unknown:#a0a0b0;--module-core:#6580f5;--module-module:#60a5fa;--module-unavailable:#ef4444;--readiness-ready:#3cb371;--readiness-warning:#f0b840;--readiness-blocked:#ef4444}}:root[data-theme=dark]{--background:#0f0f23;--surface:#1a1a2e;--surface-muted:#16213e;--foreground:#e8e8e8;--foreground-muted:#a0a0b0;--border:#2a2a3e;--border-strong:#4a4a5e;--primary:#6580f5;--primary-foreground:#fff;--success:#3cb371;--warning:#f0b840;--danger:#ef4444;--info:#60a5fa;--finding-blocking:#ef4444;--finding-warning:#f0b840;--finding-fyi:#a0a0b0;--work-item-draft:#a0a0b0;--work-item-ready:#60a5fa;--work-item-progress:#a78bfa;--work-item-blocked:#ef4444;--work-item-completed:#3cb371;--work-item-archived:#6a6a7e;--knowledge-ready:#3cb371;--knowledge-missing:#ef4444;--knowledge-placeholder:#f0b840;--knowledge-unknown:#a0a0b0;--module-core:#6580f5;--module-module:#60a5fa;--module-unavailable:#ef4444;--readiness-ready:#3cb371;--readiness-warning:#f0b840;--readiness-blocked:#ef4444}body{font-family:var(--font-sans);background:var(--background);color:var(--foreground);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0}code,.font-mono{font-family:var(--font-mono)}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>admin</title>
8
- <script type="module" crossorigin src="/assets/index-BrVaLM-z.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-zcGzKcF4.css">
8
+ <script type="module" crossorigin src="/assets/index-Bz7DGBBm.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-J0BnLZHW.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -54,8 +54,16 @@ import {
54
54
  buildProjectExplanation,
55
55
  buildReadinessReport,
56
56
  buildProjectRoute,
57
+ knowledgeLayers,
57
58
  loadConfig,
58
- loadMappedModules
59
+ loadMappedModules,
60
+ discoverKnowledge,
61
+ getWorkItems as coreGetWorkItems,
62
+ getWorkItem as coreGetWorkItem,
63
+ WorkItemNotFoundError,
64
+ exists,
65
+ join,
66
+ readFile
59
67
  } from "@kaddo/cli/core";
60
68
  function getProjectSummary(dir) {
61
69
  const config = loadConfig(dir);
@@ -90,6 +98,22 @@ function getWorkItemSummary(dir) {
90
98
  }))
91
99
  };
92
100
  }
101
+ function getWorkItemsList(dir, filters = {}) {
102
+ return coreGetWorkItems(dir, filters);
103
+ }
104
+ function getWorkItemDetail(dir, workItemId) {
105
+ if (!workItemId || workItemId.includes("..") || workItemId.includes("/") || workItemId.includes("\\") || workItemId.startsWith(".")) {
106
+ throw new CoreError("INVALID_WORK_ITEM_ID", "Invalid Work Item identifier.");
107
+ }
108
+ try {
109
+ return coreGetWorkItem(dir, workItemId);
110
+ } catch (err) {
111
+ if (err instanceof WorkItemNotFoundError) {
112
+ throw new CoreError("WORK_ITEM_NOT_FOUND", "This Work Item does not exist in the current project.");
113
+ }
114
+ throw err;
115
+ }
116
+ }
93
117
  function getModules(dir) {
94
118
  const mapped = loadMappedModules(dir);
95
119
  return {
@@ -157,6 +181,71 @@ function getProjectOverview(dir) {
157
181
  findings: getFindings(dir)
158
182
  };
159
183
  }
184
+ function getKnowledgeInventory(dir) {
185
+ const artifacts = discoverKnowledge(dir).filter((a) => !a.isWorkItem);
186
+ const layerSummary = knowledgeLayers(dir);
187
+ const layerMap = /* @__PURE__ */ new Map();
188
+ for (const ls of layerSummary) {
189
+ const id = ls.layer.toLowerCase();
190
+ layerMap.set(id, { id, label: ls.layer, status: ls.status, artifacts: [] });
191
+ }
192
+ for (const a of artifacts) {
193
+ const layerId = a.layer === "module" ? "tech" : a.layer;
194
+ if (!layerMap.has(layerId)) {
195
+ layerMap.set(layerId, { id: layerId, label: layerId.charAt(0).toUpperCase() + layerId.slice(1), status: "unknown", artifacts: [] });
196
+ }
197
+ const layer = layerMap.get(layerId);
198
+ const status = a.status || "available";
199
+ layer.artifacts.push({
200
+ id: a.id || a.relPath.replace(/[/\\]/g, "-").replace(/\.md$/, ""),
201
+ title: a.title || a.relPath.split("/").pop()?.replace(/\.md$/, "").replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) || "Untitled",
202
+ layer: layerId,
203
+ path: a.relPath,
204
+ status: normalizeArtifactStatus(status),
205
+ type: a.type || void 0
206
+ });
207
+ }
208
+ return { layers: Array.from(layerMap.values()) };
209
+ }
210
+ function normalizeArtifactStatus(status) {
211
+ if (!status || status === "active" || status === "ready") return "available";
212
+ if (status === "placeholder" || status === "draft") return "placeholder";
213
+ if (status === "missing") return "missing";
214
+ if (status === "not-applicable" || status === "n/a") return "not-applicable";
215
+ return "available";
216
+ }
217
+ function getKnowledgeArtifactDetail(dir, artifactId) {
218
+ if (artifactId.includes("..") || artifactId.startsWith("/") || artifactId.includes("\\")) {
219
+ throw new CoreError("INVALID_PATH", "Invalid artifact identifier.");
220
+ }
221
+ const artifacts = discoverKnowledge(dir).filter((a) => !a.isWorkItem);
222
+ const match = artifacts.find((a) => {
223
+ const derivedId = a.id || a.relPath.replace(/[/\\]/g, "-").replace(/\.md$/, "");
224
+ return derivedId === artifactId;
225
+ });
226
+ if (!match) {
227
+ throw new CoreError("ARTIFACT_NOT_FOUND", "Knowledge artifact not found.");
228
+ }
229
+ const fullPath = join(dir, match.relPath);
230
+ if (!exists(fullPath)) {
231
+ throw new CoreError("ARTIFACT_UNAVAILABLE", "This artifact existed when the Knowledge inventory was loaded but can no longer be read.");
232
+ }
233
+ const raw = readFile(fullPath);
234
+ const contentStart = raw.indexOf("---", raw.indexOf("---") + 3);
235
+ const content = contentStart > 0 ? raw.slice(contentStart + 3).trim() : raw;
236
+ const layerId = match.layer === "module" ? "tech" : match.layer;
237
+ const status = normalizeArtifactStatus(match.status || "available");
238
+ return {
239
+ id: match.id || match.relPath.replace(/[/\\]/g, "-").replace(/\.md$/, ""),
240
+ title: match.title || match.relPath.split("/").pop()?.replace(/\.md$/, "").replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) || "Untitled",
241
+ layer: layerId,
242
+ path: match.relPath,
243
+ status,
244
+ format: "markdown",
245
+ content,
246
+ type: match.type || void 0
247
+ };
248
+ }
160
249
  var CoreError = class extends Error {
161
250
  constructor(code, message) {
162
251
  super(message);
@@ -218,11 +307,46 @@ async function createAdminServer(opts) {
218
307
  app.get("/api/v1/admin/overview", coreRoute(getProjectOverview));
219
308
  app.get("/api/v1/admin/project", coreRoute(getProjectSummary));
220
309
  app.get("/api/v1/admin/knowledge", coreRoute(getKnowledgeSummary));
221
- app.get("/api/v1/admin/work-items", coreRoute(getWorkItemSummary));
310
+ app.get(
311
+ "/api/v1/admin/work-items",
312
+ async (request) => {
313
+ try {
314
+ const { status, module, query } = request.query;
315
+ return getWorkItemsList(projectDir, { status, module, query });
316
+ } catch (err) {
317
+ if (err instanceof CoreError) {
318
+ return { error: { code: err.code, message: err.message } };
319
+ }
320
+ throw err;
321
+ }
322
+ }
323
+ );
324
+ app.get("/api/v1/admin/work-items/:workItemId", async (request, reply) => {
325
+ try {
326
+ return getWorkItemDetail(projectDir, request.params.workItemId);
327
+ } catch (err) {
328
+ if (err instanceof CoreError) {
329
+ const code = err.code === "WORK_ITEM_NOT_FOUND" ? 404 : err.code === "INVALID_WORK_ITEM_ID" ? 400 : 500;
330
+ return reply.code(code).send({ error: { code: err.code, message: err.message } });
331
+ }
332
+ throw err;
333
+ }
334
+ });
222
335
  app.get("/api/v1/admin/modules", coreRoute(getModules));
223
336
  app.get("/api/v1/admin/readiness", coreRoute(getProjectReadiness));
224
337
  app.get("/api/v1/admin/route", coreRoute(getProjectRoute));
225
338
  app.get("/api/v1/admin/findings", coreRoute(getFindings));
339
+ app.get("/api/v1/admin/knowledge/inventory", coreRoute(getKnowledgeInventory));
340
+ app.get("/api/v1/admin/knowledge/artifact/:artifactId", async (request) => {
341
+ try {
342
+ return getKnowledgeArtifactDetail(projectDir, request.params.artifactId);
343
+ } catch (err) {
344
+ if (err instanceof CoreError) {
345
+ return { error: { code: err.code, message: err.message } };
346
+ }
347
+ throw err;
348
+ }
349
+ });
226
350
  if (staticDir) {
227
351
  app.setNotFoundHandler(async (_request, reply) => {
228
352
  return reply.sendFile("index.html");
@@ -427,6 +551,123 @@ var ProjectOverviewSchema = z.object({
427
551
  route: ProjectRouteSchema,
428
552
  findings: FindingsSummarySchema
429
553
  });
554
+ var KnowledgeArtifactSummarySchema = z.object({
555
+ id: z.string(),
556
+ title: z.string(),
557
+ layer: z.string(),
558
+ path: z.string(),
559
+ status: z.string(),
560
+ type: z.string().optional()
561
+ });
562
+ var KnowledgeInventoryLayerSchema = z.object({
563
+ id: z.string(),
564
+ label: z.string(),
565
+ status: z.string(),
566
+ artifacts: z.array(KnowledgeArtifactSummarySchema)
567
+ });
568
+ var KnowledgeInventorySchema = z.object({
569
+ layers: z.array(KnowledgeInventoryLayerSchema)
570
+ });
571
+ var KnowledgeArtifactDetailSchema = z.object({
572
+ id: z.string(),
573
+ title: z.string(),
574
+ layer: z.string(),
575
+ path: z.string(),
576
+ status: z.string(),
577
+ format: z.string(),
578
+ content: z.string(),
579
+ type: z.string().optional()
580
+ });
581
+ var WorkItemsSummaryStatsSchema = z.object({
582
+ total: z.number(),
583
+ active: z.number(),
584
+ draft: z.number(),
585
+ ready: z.number(),
586
+ inProgress: z.number(),
587
+ blocked: z.number(),
588
+ completed: z.number(),
589
+ archived: z.number()
590
+ });
591
+ var WorkItemListItemSchema = z.object({
592
+ id: z.string(),
593
+ title: z.string(),
594
+ type: z.string(),
595
+ status: z.string(),
596
+ implementationStatus: z.string().nullable(),
597
+ validationStatus: z.string().nullable(),
598
+ releaseStatus: z.string().nullable(),
599
+ affectedModules: z.array(z.string()),
600
+ scopeConfidenceLevel: z.string().nullable(),
601
+ initiative: z.string().nullable()
602
+ });
603
+ var WorkItemsListSchema = z.object({
604
+ summary: WorkItemsSummaryStatsSchema,
605
+ items: z.array(WorkItemListItemSchema),
606
+ modules: z.array(z.string())
607
+ });
608
+ var CoverageEntrySchema = z.object({ id: z.string(), status: z.string(), reason: z.string().optional() });
609
+ var ImpactEntrySchema = z.object({
610
+ surface: z.string(),
611
+ status: z.string(),
612
+ reason: z.string().optional(),
613
+ question: z.string().optional()
614
+ });
615
+ var AcceptanceCriterionSchema = z.object({ text: z.string(), checked: z.boolean().nullable() });
616
+ var ReleaseGateEntrySchema = z.object({
617
+ id: z.string(),
618
+ status: z.string(),
619
+ reason: z.string().optional(),
620
+ requiredFor: z.string().optional()
621
+ });
622
+ var CompletionExceptionEntrySchema = z.object({
623
+ id: z.string(),
624
+ status: z.string(),
625
+ reason: z.string().optional(),
626
+ category: z.string().optional(),
627
+ impact: z.string().optional()
628
+ });
629
+ var RepoValidationSchema = z.object({ command: z.string(), status: z.string(), reason: z.string().optional() });
630
+ var RepoMigrationSchema = z.object({
631
+ id: z.string(),
632
+ environment: z.string(),
633
+ status: z.string(),
634
+ reason: z.string().optional()
635
+ });
636
+ var EvidenceRepoSchema = z.object({
637
+ module: z.string(),
638
+ role: z.string(),
639
+ status: z.string(),
640
+ changedPaths: z.array(z.string()),
641
+ validations: z.array(RepoValidationSchema),
642
+ migrations: z.array(RepoMigrationSchema)
643
+ });
644
+ var LinkedDecisionSchema = z.object({
645
+ id: z.string(),
646
+ title: z.string().optional(),
647
+ knowledgeId: z.string().optional(),
648
+ knowledgeLayer: z.string().optional()
649
+ });
650
+ var LinkedKnowledgeSchema = z.object({ id: z.string(), title: z.string(), layer: z.string() });
651
+ var WorkItemDetailSchema = WorkItemListItemSchema.extend({
652
+ actor: z.string().nullable(),
653
+ outcome: z.string().nullable(),
654
+ currentBehavior: z.string().nullable(),
655
+ targetBehavior: z.string().nullable(),
656
+ entryPoints: z.string().nullable(),
657
+ endToEndFlow: z.string().nullable(),
658
+ scopeConfidence: z.object({ level: z.string(), reasons: z.array(z.string()) }).nullable(),
659
+ scopeUnknowns: z.array(z.string()),
660
+ moduleCoverage: z.array(CoverageEntrySchema),
661
+ impactAnalysis: z.array(ImpactEntrySchema),
662
+ acceptanceCriteria: z.array(AcceptanceCriterionSchema),
663
+ implementationEvidence: z.array(EvidenceRepoSchema),
664
+ releaseGates: z.array(ReleaseGateEntrySchema),
665
+ completionExceptions: z.array(CompletionExceptionEntrySchema),
666
+ decisions: z.array(LinkedDecisionSchema),
667
+ relatedKnowledge: z.array(LinkedKnowledgeSchema),
668
+ source: z.object({ type: z.string(), id: z.string().optional(), inferred: z.boolean() }).passthrough(),
669
+ path: z.string()
670
+ });
430
671
  var ErrorResponseSchema = z.object({
431
672
  error: z.object({
432
673
  code: z.string(),
@@ -436,6 +677,10 @@ var ErrorResponseSchema = z.object({
436
677
  export {
437
678
  ErrorResponseSchema,
438
679
  FindingsSummarySchema,
680
+ KnowledgeArtifactDetailSchema,
681
+ KnowledgeArtifactSummarySchema,
682
+ KnowledgeInventoryLayerSchema,
683
+ KnowledgeInventorySchema,
439
684
  KnowledgeSummarySchema,
440
685
  ModuleSummarySchema,
441
686
  ProjectOverviewSchema,
@@ -445,6 +690,10 @@ export {
445
690
  RouteStepSchema,
446
691
  SQLiteAdminStorage,
447
692
  SessionManager,
693
+ WorkItemDetailSchema,
694
+ WorkItemListItemSchema,
448
695
  WorkItemSummarySchema,
696
+ WorkItemsListSchema,
697
+ WorkItemsSummaryStatsSchema,
449
698
  createAdminServer
450
699
  };
package/dist/core.js CHANGED
@@ -7450,15 +7450,285 @@ function analyzeCrossRepoEvidence(input) {
7450
7450
  repoEvidence
7451
7451
  };
7452
7452
  }
7453
+
7454
+ // src/core/work-items.ts
7455
+ import matter6 from "gray-matter";
7456
+ var WorkItemNotFoundError = class extends Error {
7457
+ constructor(workItemId) {
7458
+ super(`Work Item "${workItemId}" was not found.`);
7459
+ this.workItemId = workItemId;
7460
+ this.name = "WorkItemNotFoundError";
7461
+ }
7462
+ workItemId;
7463
+ };
7464
+ function getWorkItemsSummary(dir) {
7465
+ return summarize2(discoverWorkItems(dir));
7466
+ }
7467
+ function summarize2(artifacts) {
7468
+ const states = artifacts.map((a) => lifecycleStateOf({ status: a.status, filePath: a.filePath }));
7469
+ const count = (s) => states.filter((x) => x === s).length;
7470
+ return {
7471
+ total: states.length,
7472
+ active: states.filter((s) => isActiveState(s)).length,
7473
+ draft: count("draft"),
7474
+ ready: count("ready"),
7475
+ inProgress: count("in-progress"),
7476
+ blocked: count("blocked"),
7477
+ completed: count("completed"),
7478
+ archived: count("archived")
7479
+ };
7480
+ }
7481
+ function getWorkItems(dir, filters = {}) {
7482
+ const artifacts = discoverWorkItems(dir);
7483
+ const summary = summarize2(artifacts);
7484
+ const allModules = /* @__PURE__ */ new Set();
7485
+ for (const a of artifacts) for (const m of a.affectedModules) allModules.add(m);
7486
+ let items = artifacts.map(toListItem);
7487
+ if (filters.status && filters.status !== "all") {
7488
+ items = items.filter((i) => i.status === filters.status);
7489
+ }
7490
+ if (filters.module && filters.module !== "all") {
7491
+ items = items.filter((i) => i.affectedModules.includes(filters.module));
7492
+ }
7493
+ if (filters.query && filters.query.trim()) {
7494
+ const q = filters.query.trim().toLowerCase();
7495
+ items = items.filter(
7496
+ (i) => i.id.toLowerCase().includes(q) || i.title.toLowerCase().includes(q) || i.affectedModules.some((m) => m.toLowerCase().includes(q))
7497
+ );
7498
+ }
7499
+ items.sort((a, b) => {
7500
+ const rank = (s) => isActiveState(s) ? 0 : s === "completed" ? 1 : 2;
7501
+ const r = rank(a.status) - rank(b.status);
7502
+ if (r !== 0) return r;
7503
+ return a.id.localeCompare(b.id, void 0, { numeric: true });
7504
+ });
7505
+ return { summary, items, modules: [...allModules].sort() };
7506
+ }
7507
+ function toListItem(a) {
7508
+ return {
7509
+ id: a.id || a.title,
7510
+ title: a.title || a.id,
7511
+ type: a.type,
7512
+ status: lifecycleStateOf({ status: a.status, filePath: a.filePath }),
7513
+ implementationStatus: a.implementationStatus || null,
7514
+ validationStatus: a.validationStatus || null,
7515
+ releaseStatus: a.releaseStatus || null,
7516
+ affectedModules: a.affectedModules,
7517
+ scopeConfidenceLevel: a.scopeConfidence?.level ?? null,
7518
+ initiative: a.initiative || null
7519
+ };
7520
+ }
7521
+ function getWorkItem(dir, workItemId) {
7522
+ const artifacts = discoverWorkItems(dir);
7523
+ const match = artifacts.find((a) => (a.id || a.title) === workItemId);
7524
+ if (!match) throw new WorkItemNotFoundError(workItemId);
7525
+ const base = toListItem(match);
7526
+ const body = readBody(match.filePath);
7527
+ const sections = splitSections(body);
7528
+ const fm = match.rawFrontmatter;
7529
+ const knowledge = discoverKnowledge(dir).filter((a) => !a.isWorkItem);
7530
+ const knowledgeById = new Map(knowledge.filter((k) => k.id).map((k) => [k.id, k]));
7531
+ return {
7532
+ ...base,
7533
+ actor: sectionText(sections, ["actor"]),
7534
+ outcome: sectionText(sections, ["actor and outcome", "outcome", "expected result"]),
7535
+ currentBehavior: sectionText(sections, ["current behavior", "current behaviour"]),
7536
+ targetBehavior: sectionText(sections, ["target behavior", "target behaviour"]),
7537
+ entryPoints: sectionText(sections, ["entry points"]),
7538
+ endToEndFlow: sectionText(sections, ["end-to-end flow", "end to end flow", "flow"]),
7539
+ scopeConfidence: match.scopeConfidence,
7540
+ scopeUnknowns: sectionBullets(sections, ["scope unknowns", "open scope questions"]),
7541
+ moduleCoverage: normalizeCoverage(match.moduleCoverage),
7542
+ impactAnalysis: normalizeImpact(match.impactAnalysis),
7543
+ acceptanceCriteria: parseAcceptanceCriteria(sections),
7544
+ implementationEvidence: parseEvidence(fm),
7545
+ releaseGates: parseReleaseGates(fm),
7546
+ completionExceptions: parseExceptions(fm),
7547
+ decisions: parseDecisions(match.decisions, knowledgeById),
7548
+ relatedKnowledge: parseRelatedKnowledge(fm, knowledgeById),
7549
+ source: parseWorkItemSource(fm),
7550
+ path: match.relPath
7551
+ };
7552
+ }
7553
+ function readBody(filePath) {
7554
+ try {
7555
+ const raw = readFile(filePath);
7556
+ return matter6(raw).content;
7557
+ } catch {
7558
+ return "";
7559
+ }
7560
+ }
7561
+ function splitSections(body) {
7562
+ const sections = /* @__PURE__ */ new Map();
7563
+ const lines = body.split(/\r?\n/);
7564
+ let current = null;
7565
+ let buffer = [];
7566
+ const flush = () => {
7567
+ if (current !== null) sections.set(current, buffer.join("\n").trim());
7568
+ buffer = [];
7569
+ };
7570
+ for (const line of lines) {
7571
+ const m = line.match(/^#{1,6}\s+(.*?)\s*$/);
7572
+ if (m) {
7573
+ flush();
7574
+ current = m[1].toLowerCase().replace(/[`*_]/g, "").trim();
7575
+ } else if (current !== null) {
7576
+ buffer.push(line);
7577
+ }
7578
+ }
7579
+ flush();
7580
+ return sections;
7581
+ }
7582
+ function isPlaceholder(text3) {
7583
+ const t = text3.trim();
7584
+ if (!t) return true;
7585
+ if (/^tbd$/i.test(t)) return true;
7586
+ if (/^_[^_]*_$/.test(t) && !t.includes("\n")) return true;
7587
+ return false;
7588
+ }
7589
+ function sectionText(sections, headings) {
7590
+ for (const h of headings) {
7591
+ const body = sections.get(h);
7592
+ if (body != null && !isPlaceholder(body)) return body.trim();
7593
+ }
7594
+ return null;
7595
+ }
7596
+ function sectionBullets(sections, headings) {
7597
+ for (const h of headings) {
7598
+ const body = sections.get(h);
7599
+ if (body == null || isPlaceholder(body)) continue;
7600
+ const bullets = extractBullets(body);
7601
+ if (bullets.length > 0) return bullets.map((b) => b.text);
7602
+ }
7603
+ return [];
7604
+ }
7605
+ function extractBullets(body) {
7606
+ const out = [];
7607
+ for (const line of body.split(/\r?\n/)) {
7608
+ const m = line.match(/^\s*[-*+]\s+(.*)$/);
7609
+ if (!m) continue;
7610
+ let text3 = m[1].trim();
7611
+ let checked = null;
7612
+ const cb = text3.match(/^\[([ xX])\]\s*(.*)$/);
7613
+ if (cb) {
7614
+ checked = cb[1].toLowerCase() === "x";
7615
+ text3 = cb[2].trim();
7616
+ }
7617
+ if (!text3 || isPlaceholder(text3)) continue;
7618
+ out.push({ text: text3, checked });
7619
+ }
7620
+ return out;
7621
+ }
7622
+ function parseAcceptanceCriteria(sections) {
7623
+ const body = sections.get("acceptance criteria");
7624
+ if (body == null || isPlaceholder(body)) return [];
7625
+ return extractBullets(body).map((b) => ({ text: b.text, checked: b.checked }));
7626
+ }
7627
+ function normalizeCoverage(mc) {
7628
+ if (!mc) return [];
7629
+ return Object.entries(mc).map(([id, v]) => ({ id, status: v.status, ...v.reason ? { reason: v.reason } : {} }));
7630
+ }
7631
+ function normalizeImpact(ia) {
7632
+ if (!ia) return [];
7633
+ return Object.entries(ia).map(([surface, v]) => ({
7634
+ surface,
7635
+ status: v.status,
7636
+ ...v.reason ? { reason: v.reason } : {},
7637
+ ...v.question ? { question: v.question } : {}
7638
+ }));
7639
+ }
7640
+ function optStr2(v) {
7641
+ if (typeof v === "string" && v.trim()) return v.trim();
7642
+ return void 0;
7643
+ }
7644
+ function parseEvidence(fm) {
7645
+ const evidence = fm.implementation_evidence;
7646
+ const repos = evidence?.repositories;
7647
+ if (!repos || typeof repos !== "object" || Array.isArray(repos)) return [];
7648
+ const out = [];
7649
+ for (const [module, val] of Object.entries(repos)) {
7650
+ if (!val || typeof val !== "object" || Array.isArray(val)) continue;
7651
+ const d = val;
7652
+ const validations = Array.isArray(d.validations) ? d.validations.map((v) => ({
7653
+ command: String(v.command ?? ""),
7654
+ status: String(v.status ?? "unknown"),
7655
+ ...optStr2(v.reason) ? { reason: optStr2(v.reason) } : {}
7656
+ })) : [];
7657
+ const migrations = Array.isArray(d.migrations) ? d.migrations.map((m) => ({
7658
+ id: String(m.id ?? ""),
7659
+ environment: String(m.environment ?? ""),
7660
+ status: String(m.status ?? "unknown"),
7661
+ ...optStr2(m.reason) ? { reason: optStr2(m.reason) } : {}
7662
+ })) : [];
7663
+ out.push({
7664
+ module,
7665
+ role: String(d.role ?? (module === "core" ? "core" : "module")),
7666
+ status: String(d.status ?? "unknown"),
7667
+ changedPaths: Array.isArray(d.changed_paths) ? d.changed_paths.map(String).filter(Boolean) : [],
7668
+ validations,
7669
+ migrations
7670
+ });
7671
+ }
7672
+ return out;
7673
+ }
7674
+ function parseReleaseGates(fm) {
7675
+ if (!Array.isArray(fm.release_gates)) return [];
7676
+ return fm.release_gates.filter((g) => g && typeof g === "object").map((g) => ({
7677
+ id: String(g.id ?? ""),
7678
+ status: String(g.status ?? "pending"),
7679
+ ...optStr2(g.reason) ? { reason: optStr2(g.reason) } : {},
7680
+ ...optStr2(g.required_for) ? { requiredFor: optStr2(g.required_for) } : {}
7681
+ }));
7682
+ }
7683
+ function parseExceptions(fm) {
7684
+ if (!Array.isArray(fm.completion_exceptions)) return [];
7685
+ return fm.completion_exceptions.filter((e) => e && typeof e === "object").map((e) => ({
7686
+ id: String(e.id ?? ""),
7687
+ status: String(e.status ?? "proposed"),
7688
+ ...optStr2(e.reason) ? { reason: optStr2(e.reason) } : {},
7689
+ ...optStr2(e.category) ? { category: optStr2(e.category) } : {},
7690
+ ...optStr2(e.impact) ? { impact: optStr2(e.impact) } : {}
7691
+ }));
7692
+ }
7693
+ function parseDecisions(decisions, knowledgeById) {
7694
+ return decisions.map((id) => {
7695
+ const k = knowledgeById.get(id);
7696
+ return {
7697
+ id,
7698
+ ...k?.title ? { title: k.title } : {},
7699
+ ...k ? { knowledgeId: k.id, knowledgeLayer: k.layer } : {}
7700
+ };
7701
+ });
7702
+ }
7703
+ function parseRelatedKnowledge(fm, knowledgeById) {
7704
+ const refs = /* @__PURE__ */ new Set();
7705
+ for (const field of ["related_knowledge", "knowledge", "related"]) {
7706
+ const v = fm[field];
7707
+ if (Array.isArray(v)) {
7708
+ for (const r of v) if (typeof r === "string" && r.trim()) refs.add(r.trim());
7709
+ }
7710
+ }
7711
+ const out = [];
7712
+ for (const ref of refs) {
7713
+ const k = knowledgeById.get(ref);
7714
+ if (k && k.id) out.push({ id: k.id, title: k.title || k.id, layer: k.layer });
7715
+ }
7716
+ return out;
7717
+ }
7453
7718
  export {
7719
+ WorkItemNotFoundError,
7454
7720
  analyzeCrossRepoEvidence,
7455
7721
  analyzeScopeCoverage,
7456
7722
  buildProjectExplanation,
7457
7723
  buildProjectRoute,
7458
7724
  buildReadinessReport,
7459
7725
  cwd,
7726
+ discoverKnowledge,
7460
7727
  discoverWorkItems,
7461
7728
  exists,
7729
+ getWorkItem,
7730
+ getWorkItems,
7731
+ getWorkItemsSummary,
7462
7732
  isActiveState,
7463
7733
  isModule,
7464
7734
  join,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/cli",
3
- "version": "3.70.1",
3
+ "version": "3.72.0",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {