mrvn-cli 0.5.23 → 0.5.25

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/marvin.js CHANGED
@@ -19522,8 +19522,30 @@ function inline(text) {
19522
19522
  s = s.replace(/__([^_]+)__/g, "<strong>$1</strong>");
19523
19523
  s = s.replace(/\*([^*]+)\*/g, "<em>$1</em>");
19524
19524
  s = s.replace(/_([^_]+)_/g, "<em>$1</em>");
19525
+ s = linkArtifactIds(s);
19525
19526
  return s;
19526
19527
  }
19528
+ var ID_PREFIX_TO_TYPE = (() => {
19529
+ const entries = [];
19530
+ for (const [type, prefix] of Object.entries(CORE_ID_PREFIXES)) {
19531
+ entries.push([prefix, type]);
19532
+ }
19533
+ for (const reg of COMMON_REGISTRATIONS) {
19534
+ if (!entries.some(([p]) => p === reg.idPrefix)) {
19535
+ entries.push([reg.idPrefix, reg.type]);
19536
+ }
19537
+ }
19538
+ entries.sort((a, b) => b[0].length - a[0].length);
19539
+ return new Map(entries);
19540
+ })();
19541
+ function linkArtifactIds(html) {
19542
+ return html.replace(/\b([A-Z]{1,3})-(\d{3,})\b/g, (match, prefix, num) => {
19543
+ const type = ID_PREFIX_TO_TYPE.get(prefix);
19544
+ if (!type) return match;
19545
+ const id = `${prefix}-${num}`;
19546
+ return `<a href="/docs/${type}/${id}" class="artifact-link">${match}</a>`;
19547
+ });
19548
+ }
19527
19549
  function layout(opts, body) {
19528
19550
  const switcherHtml = opts.personaSwitcherHtml ?? "";
19529
19551
  let navHtml;
@@ -20235,6 +20257,75 @@ tr:hover td {
20235
20257
  margin: 0.75rem 0;
20236
20258
  }
20237
20259
 
20260
+ /* Artifact cross-links */
20261
+ a.artifact-link {
20262
+ color: var(--accent);
20263
+ text-decoration: none;
20264
+ font-weight: 500;
20265
+ border-bottom: 1px dotted var(--accent);
20266
+ }
20267
+ a.artifact-link:hover {
20268
+ border-bottom-style: solid;
20269
+ }
20270
+
20271
+ /* Assessment timeline */
20272
+ .assessment-timeline {
20273
+ margin-top: 1.5rem;
20274
+ }
20275
+ .assessment-timeline h3 {
20276
+ font-size: 1rem;
20277
+ font-weight: 600;
20278
+ margin-bottom: 0.75rem;
20279
+ }
20280
+ .assessment-entry {
20281
+ background: var(--bg-card);
20282
+ border: 1px solid var(--border);
20283
+ border-radius: var(--radius);
20284
+ padding: 0.75rem 1rem;
20285
+ margin-bottom: 0.75rem;
20286
+ }
20287
+ .assessment-entry.assessment-latest {
20288
+ border-left: 3px solid var(--accent);
20289
+ }
20290
+ .assessment-header {
20291
+ display: flex;
20292
+ align-items: center;
20293
+ gap: 0.5rem;
20294
+ margin-bottom: 0.5rem;
20295
+ }
20296
+ .assessment-date {
20297
+ font-size: 0.8rem;
20298
+ color: var(--text-dim);
20299
+ font-family: var(--mono);
20300
+ }
20301
+ .assessment-comment {
20302
+ font-size: 0.875rem;
20303
+ line-height: 1.6;
20304
+ margin-bottom: 0.5rem;
20305
+ }
20306
+ .assessment-stat {
20307
+ font-size: 0.8rem;
20308
+ color: var(--text-dim);
20309
+ margin-bottom: 0.25rem;
20310
+ }
20311
+ .assessment-stat strong {
20312
+ color: var(--text);
20313
+ }
20314
+ .assessment-signals {
20315
+ list-style: none;
20316
+ padding: 0;
20317
+ margin: 0.5rem 0 0;
20318
+ }
20319
+ .assessment-signals li {
20320
+ font-size: 0.8rem;
20321
+ padding: 0.15rem 0;
20322
+ }
20323
+ .progress-bar-inline {
20324
+ font-family: var(--mono);
20325
+ font-size: 0.75rem;
20326
+ letter-spacing: -0.5px;
20327
+ }
20328
+
20238
20329
  /* Filters */
20239
20330
  .filters {
20240
20331
  display: flex;
@@ -21399,23 +21490,33 @@ function documentsPage(data) {
21399
21490
  function documentDetailPage(doc) {
21400
21491
  const fm = doc.frontmatter;
21401
21492
  const label = typeLabel(fm.type);
21402
- const skipKeys = /* @__PURE__ */ new Set(["title", "type"]);
21493
+ const skipKeys = /* @__PURE__ */ new Set(["title", "type", "assessmentHistory", "assessmentSummary"]);
21403
21494
  const entries = Object.entries(fm).filter(
21404
- ([key]) => !skipKeys.has(key) && fm[key] != null
21495
+ ([key, value]) => !skipKeys.has(key) && value != null && typeof value !== "object"
21496
+ );
21497
+ const arrayEntries = Object.entries(fm).filter(
21498
+ ([key, value]) => !skipKeys.has(key) && Array.isArray(value) && value.every((v) => typeof v === "string")
21405
21499
  );
21406
- const dtDd = entries.map(([key, value]) => {
21500
+ const allEntries = [
21501
+ ...entries.filter(([, v]) => !Array.isArray(v)),
21502
+ ...arrayEntries
21503
+ ];
21504
+ const dtDd = allEntries.map(([key, value]) => {
21407
21505
  let rendered;
21408
21506
  if (key === "status") {
21409
21507
  rendered = statusBadge(value);
21410
21508
  } else if (key === "tags" && Array.isArray(value)) {
21411
21509
  rendered = value.map((t) => `<span class="badge badge-default">${escapeHtml(t)}</span>`).join(" ");
21412
- } else if (key === "created" || key === "updated") {
21510
+ } else if (key === "created" || key === "updated" || key === "lastAssessedAt" || key === "lastJiraSyncAt") {
21413
21511
  rendered = formatDate(value);
21414
21512
  } else {
21415
- rendered = escapeHtml(String(value));
21513
+ rendered = linkArtifactIds(escapeHtml(String(value)));
21416
21514
  }
21417
21515
  return `<dt>${escapeHtml(key)}</dt><dd>${rendered}</dd>`;
21418
21516
  }).join("\n ");
21517
+ const rawHistory = Array.isArray(fm.assessmentHistory) ? fm.assessmentHistory : fm.assessmentSummary && typeof fm.assessmentSummary === "object" ? [fm.assessmentSummary] : [];
21518
+ const assessmentHistory = rawHistory.filter(isValidAssessmentEntry).sort((a, b) => (b.generatedAt ?? "").localeCompare(a.generatedAt ?? ""));
21519
+ const timelineHtml = assessmentHistory.length > 0 ? renderAssessmentTimeline(assessmentHistory) : "";
21419
21520
  return `
21420
21521
  <div class="breadcrumb">
21421
21522
  <a href="/">Overview</a><span class="sep">/</span>
@@ -21435,8 +21536,73 @@ function documentDetailPage(doc) {
21435
21536
  </div>
21436
21537
 
21437
21538
  ${doc.content.trim() ? `<div class="detail-content">${renderMarkdown(doc.content)}</div>` : ""}
21539
+
21540
+ ${timelineHtml}
21438
21541
  `;
21439
21542
  }
21543
+ function isValidAssessmentEntry(value) {
21544
+ if (typeof value !== "object" || value === null) return false;
21545
+ const obj = value;
21546
+ if (typeof obj.generatedAt !== "string") return false;
21547
+ if (obj.signals !== void 0 && !Array.isArray(obj.signals)) return false;
21548
+ return true;
21549
+ }
21550
+ function normalizeEntry(entry) {
21551
+ return {
21552
+ generatedAt: entry.generatedAt ?? "",
21553
+ commentSummary: typeof entry.commentSummary === "string" ? entry.commentSummary : null,
21554
+ commentAnalysisProgress: typeof entry.commentAnalysisProgress === "number" ? entry.commentAnalysisProgress : null,
21555
+ signals: Array.isArray(entry.signals) ? entry.signals.filter((s) => typeof s === "string") : [],
21556
+ childCount: typeof entry.childCount === "number" ? entry.childCount : 0,
21557
+ childDoneCount: typeof entry.childDoneCount === "number" ? entry.childDoneCount : 0,
21558
+ childRollupProgress: typeof entry.childRollupProgress === "number" ? entry.childRollupProgress : null,
21559
+ linkedIssueCount: typeof entry.linkedIssueCount === "number" ? entry.linkedIssueCount : 0
21560
+ };
21561
+ }
21562
+ function renderAssessmentTimeline(history) {
21563
+ const entries = history.map((raw, i) => {
21564
+ const entry = normalizeEntry(raw);
21565
+ const date5 = entry.generatedAt ? formatDate(entry.generatedAt) : "Unknown date";
21566
+ const time3 = entry.generatedAt?.slice(11, 16) ?? "";
21567
+ const isLatest = i === 0;
21568
+ const parts = [];
21569
+ if (entry.commentSummary) {
21570
+ parts.push(`<div class="assessment-comment">${linkArtifactIds(escapeHtml(entry.commentSummary))}</div>`);
21571
+ }
21572
+ if (entry.commentAnalysisProgress !== null) {
21573
+ parts.push(`<div class="assessment-stat">\u{1F4CA} Comment-derived progress: <strong>${entry.commentAnalysisProgress}%</strong></div>`);
21574
+ }
21575
+ if (entry.childCount > 0) {
21576
+ const bar = progressBarHtml(entry.childRollupProgress ?? 0);
21577
+ parts.push(`<div class="assessment-stat">\u{1F476} Children: ${entry.childDoneCount}/${entry.childCount} done ${bar} ${entry.childRollupProgress ?? 0}%</div>`);
21578
+ }
21579
+ if (entry.linkedIssueCount > 0) {
21580
+ parts.push(`<div class="assessment-stat">\u{1F517} Linked issues: ${entry.linkedIssueCount}</div>`);
21581
+ }
21582
+ if (entry.signals.length > 0) {
21583
+ const signalItems = entry.signals.map((s) => `<li>${linkArtifactIds(escapeHtml(s))}</li>`).join("");
21584
+ parts.push(`<ul class="assessment-signals">${signalItems}</ul>`);
21585
+ }
21586
+ return `
21587
+ <div class="assessment-entry${isLatest ? " assessment-latest" : ""}">
21588
+ <div class="assessment-header">
21589
+ <span class="assessment-date">${escapeHtml(date5)} ${escapeHtml(time3)}</span>
21590
+ ${isLatest ? '<span class="badge badge-default">Latest</span>' : ""}
21591
+ </div>
21592
+ ${parts.join("\n")}
21593
+ </div>`;
21594
+ });
21595
+ return `
21596
+ <div class="assessment-timeline">
21597
+ <h3>Assessment History</h3>
21598
+ ${entries.join("\n")}
21599
+ </div>`;
21600
+ }
21601
+ function progressBarHtml(pct) {
21602
+ const filled = Math.round(Math.max(0, Math.min(100, pct)) / 10);
21603
+ const empty = 10 - filled;
21604
+ return `<span class="progress-bar-inline">${"\u2588".repeat(filled)}${"\u2591".repeat(empty)}</span>`;
21605
+ }
21440
21606
 
21441
21607
  // src/web/persona-views.ts
21442
21608
  var VIEWS = /* @__PURE__ */ new Map();
@@ -27051,6 +27217,41 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
27051
27217
  }
27052
27218
  }
27053
27219
  }
27220
+ if (options.applyUpdates) {
27221
+ const newEntry = buildAssessmentSummary(
27222
+ commentSummary,
27223
+ commentAnalysisProgress,
27224
+ signals,
27225
+ children,
27226
+ linkedIssues
27227
+ );
27228
+ const existingHistory = Array.isArray(fm.assessmentHistory) ? fm.assessmentHistory : [];
27229
+ const legacySummary = fm.assessmentSummary;
27230
+ const allEntries = [newEntry, ...existingHistory];
27231
+ if (legacySummary?.generatedAt) {
27232
+ allEntries.push(legacySummary);
27233
+ }
27234
+ const MAX_HISTORY = 100;
27235
+ const seen = /* @__PURE__ */ new Set();
27236
+ const assessmentHistory = allEntries.filter((entry) => {
27237
+ if (!entry.generatedAt) return false;
27238
+ if (seen.has(entry.generatedAt)) return false;
27239
+ seen.add(entry.generatedAt);
27240
+ return true;
27241
+ }).sort((a, b) => (b.generatedAt ?? "").localeCompare(a.generatedAt ?? "")).slice(0, MAX_HISTORY);
27242
+ try {
27243
+ const payload = {
27244
+ assessmentHistory,
27245
+ lastAssessedAt: newEntry.generatedAt
27246
+ };
27247
+ if (fm.assessmentSummary !== void 0) {
27248
+ payload.assessmentSummary = void 0;
27249
+ }
27250
+ store.update(fm.id, payload);
27251
+ } catch (err) {
27252
+ errors.push(`Failed to persist assessment history: ${err instanceof Error ? err.message : String(err)}`);
27253
+ }
27254
+ }
27054
27255
  return {
27055
27256
  artifactId: fm.id,
27056
27257
  title: fm.title,
@@ -27257,6 +27458,33 @@ function emptyArtifactReport(artifactId, errors) {
27257
27458
  errors
27258
27459
  };
27259
27460
  }
27461
+ function buildAssessmentSummary(commentSummary, commentAnalysisProgress, signals, children, linkedIssues) {
27462
+ const childProgressValues = children.map((c) => {
27463
+ const updates = c.appliedUpdates.length > 0 ? c.appliedUpdates : c.proposedUpdates;
27464
+ const lastStatus = findLast(updates, (u) => u.field === "status");
27465
+ if (lastStatus && PROGRESS_DONE_STATUSES.has(String(lastStatus.proposedValue))) return 100;
27466
+ const lastProgress = findLast(updates, (u) => u.field === "progress");
27467
+ if (lastProgress) return lastProgress.proposedValue;
27468
+ return c.marvinProgress;
27469
+ });
27470
+ const childDoneCount = children.filter((c, i) => {
27471
+ const updates = c.appliedUpdates.length > 0 ? c.appliedUpdates : c.proposedUpdates;
27472
+ const lastStatus = findLast(updates, (u) => u.field === "status");
27473
+ const effectiveStatus = lastStatus ? String(lastStatus.proposedValue) : c.marvinStatus;
27474
+ return DONE_STATUSES15.has(effectiveStatus);
27475
+ }).length;
27476
+ const childRollupProgress = children.length > 0 ? Math.round(childProgressValues.reduce((s, p) => s + p, 0) / childProgressValues.length) : null;
27477
+ return {
27478
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
27479
+ commentSummary,
27480
+ commentAnalysisProgress,
27481
+ signals,
27482
+ childCount: children.length,
27483
+ childDoneCount,
27484
+ childRollupProgress,
27485
+ linkedIssueCount: linkedIssues.length
27486
+ };
27487
+ }
27260
27488
  function formatArtifactReport(report) {
27261
27489
  const parts = [];
27262
27490
  parts.push(`# Artifact Assessment \u2014 ${report.artifactId}`);
@@ -33617,7 +33845,7 @@ function createProgram() {
33617
33845
  const program2 = new Command();
33618
33846
  program2.name("marvin").description(
33619
33847
  "AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
33620
- ).version("0.5.23");
33848
+ ).version("0.5.25");
33621
33849
  program2.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
33622
33850
  await initCommand();
33623
33851
  });