mrvn-cli 0.3.2 → 0.3.3

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.
@@ -6474,13 +6474,13 @@ var error16 = () => {
6474
6474
  // no unit
6475
6475
  };
6476
6476
  const typeEntry = (t) => t ? TypeNames[t] : void 0;
6477
- const typeLabel = (t) => {
6477
+ const typeLabel2 = (t) => {
6478
6478
  const e = typeEntry(t);
6479
6479
  if (e)
6480
6480
  return e.label;
6481
6481
  return t ?? TypeNames.unknown.label;
6482
6482
  };
6483
- const withDefinite = (t) => `\u05D4${typeLabel(t)}`;
6483
+ const withDefinite = (t) => `\u05D4${typeLabel2(t)}`;
6484
6484
  const verbFor = (t) => {
6485
6485
  const e = typeEntry(t);
6486
6486
  const gender = e?.gender ?? "m";
@@ -6530,7 +6530,7 @@ var error16 = () => {
6530
6530
  switch (issue2.code) {
6531
6531
  case "invalid_type": {
6532
6532
  const expectedKey = issue2.expected;
6533
- const expected = TypeDictionary[expectedKey ?? ""] ?? typeLabel(expectedKey);
6533
+ const expected = TypeDictionary[expectedKey ?? ""] ?? typeLabel2(expectedKey);
6534
6534
  const receivedType = parsedType(issue2.input);
6535
6535
  const received = TypeDictionary[receivedType] ?? TypeNames[receivedType]?.label ?? receivedType;
6536
6536
  if (/^[A-Z]/.test(issue2.expected)) {
@@ -17934,7 +17934,7 @@ ${fragment}`);
17934
17934
  }
17935
17935
 
17936
17936
  // src/skills/action-tools.ts
17937
- import { tool as tool20 } from "@anthropic-ai/claude-agent-sdk";
17937
+ import { tool as tool21 } from "@anthropic-ai/claude-agent-sdk";
17938
17938
 
17939
17939
  // src/skills/action-runner.ts
17940
17940
  import { query } from "@anthropic-ai/claude-agent-sdk";
@@ -17943,6 +17943,1172 @@ import { query } from "@anthropic-ai/claude-agent-sdk";
17943
17943
  import {
17944
17944
  createSdkMcpServer
17945
17945
  } from "@anthropic-ai/claude-agent-sdk";
17946
+
17947
+ // src/agent/tools/web.ts
17948
+ import * as http2 from "http";
17949
+ import { tool as tool20 } from "@anthropic-ai/claude-agent-sdk";
17950
+
17951
+ // src/web/data.ts
17952
+ function getOverviewData(store) {
17953
+ const types = [];
17954
+ const counts = store.counts();
17955
+ for (const type of store.registeredTypes) {
17956
+ const total = counts[type] ?? 0;
17957
+ const open = store.list({ type, status: "open" }).length;
17958
+ types.push({ type, total, open });
17959
+ }
17960
+ const allDocs = store.list();
17961
+ const sorted = allDocs.sort(
17962
+ (a, b) => (b.frontmatter.updated ?? b.frontmatter.created).localeCompare(
17963
+ a.frontmatter.updated ?? a.frontmatter.created
17964
+ )
17965
+ );
17966
+ return { types, recent: sorted.slice(0, 20) };
17967
+ }
17968
+ function getDocumentListData(store, type, filterStatus, filterOwner) {
17969
+ if (!store.registeredTypes.includes(type)) return void 0;
17970
+ const allOfType = store.list({ type });
17971
+ const statuses = [...new Set(allOfType.map((d) => d.frontmatter.status))].sort();
17972
+ const owners = [
17973
+ ...new Set(allOfType.map((d) => d.frontmatter.owner).filter(Boolean))
17974
+ ].sort();
17975
+ let docs = allOfType;
17976
+ if (filterStatus) {
17977
+ docs = docs.filter((d) => d.frontmatter.status === filterStatus);
17978
+ }
17979
+ if (filterOwner) {
17980
+ docs = docs.filter((d) => d.frontmatter.owner === filterOwner);
17981
+ }
17982
+ docs.sort((a, b) => a.frontmatter.id.localeCompare(b.frontmatter.id));
17983
+ return { type, docs, statuses, owners, filterStatus, filterOwner };
17984
+ }
17985
+ function getDocumentDetail(store, type, id) {
17986
+ if (!store.registeredTypes.includes(type)) return void 0;
17987
+ return store.get(id);
17988
+ }
17989
+ function getGarData(store, projectName) {
17990
+ const metrics = collectGarMetrics(store);
17991
+ return evaluateGar(projectName, metrics);
17992
+ }
17993
+ function getBoardData(store, type) {
17994
+ const docs = type ? store.list({ type }) : store.list();
17995
+ const types = store.registeredTypes;
17996
+ const byStatus = /* @__PURE__ */ new Map();
17997
+ for (const doc of docs) {
17998
+ const status = doc.frontmatter.status;
17999
+ if (!byStatus.has(status)) byStatus.set(status, []);
18000
+ byStatus.get(status).push(doc);
18001
+ }
18002
+ const statusOrder = ["open", "draft", "in-progress", "blocked"];
18003
+ const allStatuses = [...byStatus.keys()];
18004
+ const ordered = [];
18005
+ for (const s of statusOrder) {
18006
+ if (allStatuses.includes(s)) ordered.push(s);
18007
+ }
18008
+ for (const s of allStatuses.sort()) {
18009
+ if (!ordered.includes(s) && s !== "done" && s !== "closed" && s !== "resolved") {
18010
+ ordered.push(s);
18011
+ }
18012
+ }
18013
+ for (const s of ["done", "closed", "resolved"]) {
18014
+ if (allStatuses.includes(s)) ordered.push(s);
18015
+ }
18016
+ const columns = ordered.map((status) => ({
18017
+ status,
18018
+ docs: byStatus.get(status) ?? []
18019
+ }));
18020
+ return { columns, type, types };
18021
+ }
18022
+
18023
+ // src/web/templates/layout.ts
18024
+ function escapeHtml(str) {
18025
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
18026
+ }
18027
+ function statusBadge(status) {
18028
+ const cls = {
18029
+ open: "badge-open",
18030
+ done: "badge-done",
18031
+ closed: "badge-done",
18032
+ resolved: "badge-resolved",
18033
+ "in-progress": "badge-in-progress",
18034
+ "in progress": "badge-in-progress",
18035
+ draft: "badge-draft",
18036
+ blocked: "badge-blocked"
18037
+ }[status.toLowerCase()] ?? "badge-default";
18038
+ return `<span class="badge ${cls}">${escapeHtml(status)}</span>`;
18039
+ }
18040
+ function formatDate(iso) {
18041
+ if (!iso) return "";
18042
+ return iso.slice(0, 10);
18043
+ }
18044
+ function typeLabel(type) {
18045
+ return type.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
18046
+ }
18047
+ function renderMarkdown(md) {
18048
+ const lines = md.split("\n");
18049
+ const out = [];
18050
+ let inList = false;
18051
+ let listTag = "ul";
18052
+ for (const raw of lines) {
18053
+ const line = raw;
18054
+ if (inList && !/^\s*[-*]\s/.test(line) && !/^\s*\d+\.\s/.test(line) && line.trim() !== "") {
18055
+ out.push(`</${listTag}>`);
18056
+ inList = false;
18057
+ }
18058
+ const headingMatch = line.match(/^(#{1,3})\s+(.+)$/);
18059
+ if (headingMatch) {
18060
+ const level = headingMatch[1].length;
18061
+ out.push(`<h${level}>${inline(headingMatch[2])}</h${level}>`);
18062
+ continue;
18063
+ }
18064
+ const ulMatch = line.match(/^\s*[-*]\s+(.+)$/);
18065
+ if (ulMatch) {
18066
+ if (!inList || listTag !== "ul") {
18067
+ if (inList) out.push(`</${listTag}>`);
18068
+ out.push("<ul>");
18069
+ inList = true;
18070
+ listTag = "ul";
18071
+ }
18072
+ out.push(`<li>${inline(ulMatch[1])}</li>`);
18073
+ continue;
18074
+ }
18075
+ const olMatch = line.match(/^\s*\d+\.\s+(.+)$/);
18076
+ if (olMatch) {
18077
+ if (!inList || listTag !== "ol") {
18078
+ if (inList) out.push(`</${listTag}>`);
18079
+ out.push("<ol>");
18080
+ inList = true;
18081
+ listTag = "ol";
18082
+ }
18083
+ out.push(`<li>${inline(olMatch[1])}</li>`);
18084
+ continue;
18085
+ }
18086
+ if (line.trim() === "") {
18087
+ if (inList) {
18088
+ out.push(`</${listTag}>`);
18089
+ inList = false;
18090
+ }
18091
+ continue;
18092
+ }
18093
+ out.push(`<p>${inline(line)}</p>`);
18094
+ }
18095
+ if (inList) out.push(`</${listTag}>`);
18096
+ return out.join("\n");
18097
+ }
18098
+ function inline(text) {
18099
+ let s = escapeHtml(text);
18100
+ s = s.replace(/`([^`]+)`/g, "<code>$1</code>");
18101
+ s = s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
18102
+ s = s.replace(/__([^_]+)__/g, "<strong>$1</strong>");
18103
+ s = s.replace(/\*([^*]+)\*/g, "<em>$1</em>");
18104
+ s = s.replace(/_([^_]+)_/g, "<em>$1</em>");
18105
+ return s;
18106
+ }
18107
+ function layout(opts, body) {
18108
+ const topItems = [
18109
+ { href: "/", label: "Overview" },
18110
+ { href: "/board", label: "Board" },
18111
+ { href: "/gar", label: "GAR Report" }
18112
+ ];
18113
+ const isActive = (href) => opts.activePath === href || href !== "/" && opts.activePath.startsWith(href) ? " active" : "";
18114
+ const groupsHtml = opts.navGroups.map((group) => {
18115
+ const links = group.types.map((type) => {
18116
+ const href = `/docs/${type}`;
18117
+ return `<a href="${href}" class="${isActive(href)}">${typeLabel(type)}s</a>`;
18118
+ }).join("\n ");
18119
+ return `
18120
+ <div class="nav-group">
18121
+ <div class="nav-group-label">${escapeHtml(group.label)}</div>
18122
+ ${links}
18123
+ </div>`;
18124
+ }).join("\n");
18125
+ return `<!DOCTYPE html>
18126
+ <html lang="en">
18127
+ <head>
18128
+ <meta charset="UTF-8">
18129
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
18130
+ <title>${escapeHtml(opts.title)} \u2014 Marvin</title>
18131
+ <link rel="stylesheet" href="/styles.css">
18132
+ </head>
18133
+ <body>
18134
+ <div class="shell">
18135
+ <aside class="sidebar">
18136
+ <div class="sidebar-brand">
18137
+ <h1>Marvin</h1>
18138
+ <div class="project-name">${escapeHtml(opts.projectName)}</div>
18139
+ </div>
18140
+ <nav>
18141
+ ${topItems.map((n) => `<a href="${n.href}" class="${isActive(n.href)}">${n.label}</a>`).join("\n ")}
18142
+ ${groupsHtml}
18143
+ </nav>
18144
+ </aside>
18145
+ <main class="main">
18146
+ ${body}
18147
+ </main>
18148
+ </div>
18149
+ </body>
18150
+ </html>`;
18151
+ }
18152
+
18153
+ // src/web/templates/styles.ts
18154
+ function renderStyles() {
18155
+ return `
18156
+ :root {
18157
+ --bg: #0f1117;
18158
+ --bg-card: #1a1d27;
18159
+ --bg-hover: #222632;
18160
+ --border: #2a2e3a;
18161
+ --text: #e1e4ea;
18162
+ --text-dim: #8b8fa4;
18163
+ --accent: #6c8cff;
18164
+ --accent-dim: #4a6ad4;
18165
+ --green: #34d399;
18166
+ --amber: #fbbf24;
18167
+ --red: #f87171;
18168
+ --radius: 8px;
18169
+ --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
18170
+ --mono: "SF Mono", "Fira Code", monospace;
18171
+ }
18172
+
18173
+ * { margin: 0; padding: 0; box-sizing: border-box; }
18174
+
18175
+ body {
18176
+ font-family: var(--font);
18177
+ background: var(--bg);
18178
+ color: var(--text);
18179
+ line-height: 1.6;
18180
+ min-height: 100vh;
18181
+ }
18182
+
18183
+ a { color: var(--accent); text-decoration: none; }
18184
+ a:hover { text-decoration: underline; }
18185
+
18186
+ /* Layout */
18187
+ .shell {
18188
+ display: flex;
18189
+ min-height: 100vh;
18190
+ }
18191
+
18192
+ .sidebar {
18193
+ width: 220px;
18194
+ background: var(--bg-card);
18195
+ border-right: 1px solid var(--border);
18196
+ padding: 1.5rem 0;
18197
+ position: fixed;
18198
+ top: 0;
18199
+ left: 0;
18200
+ bottom: 0;
18201
+ overflow-y: auto;
18202
+ }
18203
+
18204
+ .sidebar-brand {
18205
+ padding: 0 1.25rem 1.25rem;
18206
+ border-bottom: 1px solid var(--border);
18207
+ margin-bottom: 1rem;
18208
+ }
18209
+
18210
+ .sidebar-brand h1 {
18211
+ font-size: 1.1rem;
18212
+ font-weight: 700;
18213
+ color: var(--accent);
18214
+ letter-spacing: -0.02em;
18215
+ }
18216
+
18217
+ .sidebar-brand .project-name {
18218
+ font-size: 0.75rem;
18219
+ color: var(--text-dim);
18220
+ margin-top: 0.25rem;
18221
+ }
18222
+
18223
+ .sidebar nav a {
18224
+ display: block;
18225
+ padding: 0.5rem 1.25rem;
18226
+ color: var(--text-dim);
18227
+ font-size: 0.875rem;
18228
+ transition: background 0.15s, color 0.15s;
18229
+ }
18230
+
18231
+ .sidebar nav a:hover {
18232
+ background: var(--bg-hover);
18233
+ color: var(--text);
18234
+ text-decoration: none;
18235
+ }
18236
+
18237
+ .sidebar nav a.active {
18238
+ color: var(--accent);
18239
+ background: rgba(108, 140, 255, 0.08);
18240
+ border-right: 2px solid var(--accent);
18241
+ }
18242
+
18243
+ .nav-group {
18244
+ margin-top: 0.75rem;
18245
+ padding-top: 0.75rem;
18246
+ border-top: 1px solid var(--border);
18247
+ }
18248
+
18249
+ .nav-group-label {
18250
+ padding: 0.25rem 1.25rem 0.25rem;
18251
+ font-size: 0.65rem;
18252
+ text-transform: uppercase;
18253
+ letter-spacing: 0.08em;
18254
+ color: var(--text-dim);
18255
+ font-weight: 600;
18256
+ }
18257
+
18258
+ .main {
18259
+ margin-left: 220px;
18260
+ flex: 1;
18261
+ padding: 2rem 2.5rem;
18262
+ max-width: 1200px;
18263
+ }
18264
+
18265
+ /* Page header */
18266
+ .page-header {
18267
+ margin-bottom: 2rem;
18268
+ }
18269
+
18270
+ .page-header h2 {
18271
+ font-size: 1.5rem;
18272
+ font-weight: 600;
18273
+ }
18274
+
18275
+ .page-header .subtitle {
18276
+ color: var(--text-dim);
18277
+ font-size: 0.875rem;
18278
+ margin-top: 0.25rem;
18279
+ }
18280
+
18281
+ /* Breadcrumb */
18282
+ .breadcrumb {
18283
+ font-size: 0.8rem;
18284
+ color: var(--text-dim);
18285
+ margin-bottom: 1rem;
18286
+ }
18287
+
18288
+ .breadcrumb a { color: var(--text-dim); }
18289
+ .breadcrumb a:hover { color: var(--accent); }
18290
+ .breadcrumb .sep { margin: 0 0.4rem; }
18291
+
18292
+ /* Cards grid */
18293
+ .cards {
18294
+ display: grid;
18295
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
18296
+ gap: 1rem;
18297
+ margin-bottom: 2rem;
18298
+ }
18299
+
18300
+ .card {
18301
+ background: var(--bg-card);
18302
+ border: 1px solid var(--border);
18303
+ border-radius: var(--radius);
18304
+ padding: 1.25rem;
18305
+ transition: border-color 0.15s;
18306
+ }
18307
+
18308
+ .card:hover {
18309
+ border-color: var(--accent-dim);
18310
+ }
18311
+
18312
+ .card a { color: inherit; text-decoration: none; display: block; }
18313
+
18314
+ .card .card-label {
18315
+ font-size: 0.75rem;
18316
+ text-transform: uppercase;
18317
+ letter-spacing: 0.05em;
18318
+ color: var(--text-dim);
18319
+ margin-bottom: 0.5rem;
18320
+ }
18321
+
18322
+ .card .card-value {
18323
+ font-size: 1.75rem;
18324
+ font-weight: 700;
18325
+ }
18326
+
18327
+ .card .card-sub {
18328
+ font-size: 0.8rem;
18329
+ color: var(--text-dim);
18330
+ margin-top: 0.25rem;
18331
+ }
18332
+
18333
+ /* Status badge */
18334
+ .badge {
18335
+ display: inline-block;
18336
+ padding: 0.15rem 0.6rem;
18337
+ border-radius: 999px;
18338
+ font-size: 0.7rem;
18339
+ font-weight: 600;
18340
+ text-transform: uppercase;
18341
+ letter-spacing: 0.03em;
18342
+ }
18343
+
18344
+ .badge-open { background: rgba(108, 140, 255, 0.15); color: var(--accent); }
18345
+ .badge-done { background: rgba(52, 211, 153, 0.15); color: var(--green); }
18346
+ .badge-in-progress { background: rgba(251, 191, 36, 0.15); color: var(--amber); }
18347
+ .badge-draft { background: rgba(139, 143, 164, 0.15); color: var(--text-dim); }
18348
+ .badge-closed, .badge-resolved { background: rgba(52, 211, 153, 0.15); color: var(--green); }
18349
+ .badge-blocked { background: rgba(248, 113, 113, 0.15); color: var(--red); }
18350
+ .badge-default { background: rgba(139, 143, 164, 0.1); color: var(--text-dim); }
18351
+
18352
+ /* Table */
18353
+ .table-wrap {
18354
+ overflow-x: auto;
18355
+ }
18356
+
18357
+ table {
18358
+ width: 100%;
18359
+ border-collapse: collapse;
18360
+ }
18361
+
18362
+ th {
18363
+ text-align: left;
18364
+ padding: 0.6rem 0.75rem;
18365
+ font-size: 0.7rem;
18366
+ text-transform: uppercase;
18367
+ letter-spacing: 0.05em;
18368
+ color: var(--text-dim);
18369
+ border-bottom: 1px solid var(--border);
18370
+ }
18371
+
18372
+ td {
18373
+ padding: 0.6rem 0.75rem;
18374
+ font-size: 0.875rem;
18375
+ border-bottom: 1px solid var(--border);
18376
+ }
18377
+
18378
+ tr:hover td {
18379
+ background: var(--bg-hover);
18380
+ }
18381
+
18382
+ /* GAR */
18383
+ .gar-overall {
18384
+ text-align: center;
18385
+ padding: 2rem;
18386
+ margin-bottom: 2rem;
18387
+ border-radius: var(--radius);
18388
+ border: 1px solid var(--border);
18389
+ background: var(--bg-card);
18390
+ }
18391
+
18392
+ .gar-overall .dot {
18393
+ width: 60px;
18394
+ height: 60px;
18395
+ border-radius: 50%;
18396
+ display: inline-block;
18397
+ margin-bottom: 0.75rem;
18398
+ }
18399
+
18400
+ .gar-overall .label {
18401
+ font-size: 1.1rem;
18402
+ font-weight: 600;
18403
+ text-transform: uppercase;
18404
+ }
18405
+
18406
+ .gar-areas {
18407
+ display: grid;
18408
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
18409
+ gap: 1rem;
18410
+ }
18411
+
18412
+ .gar-area {
18413
+ background: var(--bg-card);
18414
+ border: 1px solid var(--border);
18415
+ border-radius: var(--radius);
18416
+ padding: 1.25rem;
18417
+ }
18418
+
18419
+ .gar-area .area-header {
18420
+ display: flex;
18421
+ align-items: center;
18422
+ gap: 0.6rem;
18423
+ margin-bottom: 0.75rem;
18424
+ }
18425
+
18426
+ .gar-area .area-dot {
18427
+ width: 14px;
18428
+ height: 14px;
18429
+ border-radius: 50%;
18430
+ flex-shrink: 0;
18431
+ }
18432
+
18433
+ .gar-area .area-name {
18434
+ font-weight: 600;
18435
+ font-size: 1rem;
18436
+ }
18437
+
18438
+ .gar-area .area-summary {
18439
+ font-size: 0.85rem;
18440
+ color: var(--text-dim);
18441
+ margin-bottom: 0.75rem;
18442
+ }
18443
+
18444
+ .gar-area ul {
18445
+ list-style: none;
18446
+ font-size: 0.8rem;
18447
+ }
18448
+
18449
+ .gar-area li {
18450
+ padding: 0.2rem 0;
18451
+ color: var(--text-dim);
18452
+ }
18453
+
18454
+ .gar-area li .ref-id {
18455
+ color: var(--accent);
18456
+ font-family: var(--mono);
18457
+ margin-right: 0.4rem;
18458
+ }
18459
+
18460
+ .dot-green { background: var(--green); }
18461
+ .dot-amber { background: var(--amber); }
18462
+ .dot-red { background: var(--red); }
18463
+
18464
+ /* Board / Kanban */
18465
+ .board {
18466
+ display: flex;
18467
+ gap: 1rem;
18468
+ overflow-x: auto;
18469
+ padding-bottom: 1rem;
18470
+ }
18471
+
18472
+ .board-column {
18473
+ min-width: 240px;
18474
+ max-width: 300px;
18475
+ flex: 1;
18476
+ }
18477
+
18478
+ .board-column-header {
18479
+ font-size: 0.75rem;
18480
+ text-transform: uppercase;
18481
+ letter-spacing: 0.05em;
18482
+ color: var(--text-dim);
18483
+ padding: 0.5rem 0.75rem;
18484
+ border-bottom: 2px solid var(--border);
18485
+ margin-bottom: 0.5rem;
18486
+ display: flex;
18487
+ justify-content: space-between;
18488
+ }
18489
+
18490
+ .board-column-header .count {
18491
+ background: var(--bg-hover);
18492
+ padding: 0 0.5rem;
18493
+ border-radius: 999px;
18494
+ font-size: 0.7rem;
18495
+ }
18496
+
18497
+ .board-card {
18498
+ background: var(--bg-card);
18499
+ border: 1px solid var(--border);
18500
+ border-radius: var(--radius);
18501
+ padding: 0.75rem;
18502
+ margin-bottom: 0.5rem;
18503
+ transition: border-color 0.15s;
18504
+ }
18505
+
18506
+ .board-card:hover {
18507
+ border-color: var(--accent-dim);
18508
+ }
18509
+
18510
+ .board-card .bc-id {
18511
+ font-family: var(--mono);
18512
+ font-size: 0.7rem;
18513
+ color: var(--accent);
18514
+ }
18515
+
18516
+ .board-card .bc-title {
18517
+ font-size: 0.85rem;
18518
+ margin: 0.25rem 0;
18519
+ }
18520
+
18521
+ .board-card .bc-owner {
18522
+ font-size: 0.7rem;
18523
+ color: var(--text-dim);
18524
+ }
18525
+
18526
+ /* Detail page */
18527
+ .detail-meta {
18528
+ background: var(--bg-card);
18529
+ border: 1px solid var(--border);
18530
+ border-radius: var(--radius);
18531
+ padding: 1.25rem;
18532
+ margin-bottom: 1.5rem;
18533
+ }
18534
+
18535
+ .detail-meta dl {
18536
+ display: grid;
18537
+ grid-template-columns: 120px 1fr;
18538
+ gap: 0.4rem 1rem;
18539
+ }
18540
+
18541
+ .detail-meta dt {
18542
+ font-size: 0.75rem;
18543
+ text-transform: uppercase;
18544
+ letter-spacing: 0.05em;
18545
+ color: var(--text-dim);
18546
+ }
18547
+
18548
+ .detail-meta dd {
18549
+ font-size: 0.875rem;
18550
+ }
18551
+
18552
+ .detail-content {
18553
+ background: var(--bg-card);
18554
+ border: 1px solid var(--border);
18555
+ border-radius: var(--radius);
18556
+ padding: 1.5rem;
18557
+ line-height: 1.7;
18558
+ }
18559
+
18560
+ .detail-content h1, .detail-content h2, .detail-content h3 {
18561
+ margin: 1.25rem 0 0.5rem;
18562
+ font-weight: 600;
18563
+ }
18564
+
18565
+ .detail-content h1 { font-size: 1.3rem; }
18566
+ .detail-content h2 { font-size: 1.15rem; }
18567
+ .detail-content h3 { font-size: 1rem; }
18568
+ .detail-content p { margin-bottom: 0.75rem; }
18569
+ .detail-content ul, .detail-content ol { margin: 0.5rem 0 0.75rem 1.5rem; }
18570
+ .detail-content li { margin-bottom: 0.25rem; }
18571
+ .detail-content code {
18572
+ background: var(--bg-hover);
18573
+ padding: 0.1rem 0.35rem;
18574
+ border-radius: 3px;
18575
+ font-family: var(--mono);
18576
+ font-size: 0.85em;
18577
+ }
18578
+
18579
+ /* Filters */
18580
+ .filters {
18581
+ display: flex;
18582
+ gap: 0.75rem;
18583
+ margin-bottom: 1.5rem;
18584
+ flex-wrap: wrap;
18585
+ }
18586
+
18587
+ .filters select {
18588
+ background: var(--bg-card);
18589
+ border: 1px solid var(--border);
18590
+ color: var(--text);
18591
+ padding: 0.4rem 0.75rem;
18592
+ border-radius: var(--radius);
18593
+ font-size: 0.8rem;
18594
+ cursor: pointer;
18595
+ }
18596
+
18597
+ .filters select:focus {
18598
+ outline: none;
18599
+ border-color: var(--accent);
18600
+ }
18601
+
18602
+ /* Empty state */
18603
+ .empty {
18604
+ text-align: center;
18605
+ padding: 3rem;
18606
+ color: var(--text-dim);
18607
+ }
18608
+
18609
+ .empty p { font-size: 0.9rem; }
18610
+
18611
+ /* Section heading */
18612
+ .section-title {
18613
+ font-size: 0.9rem;
18614
+ font-weight: 600;
18615
+ margin: 1.5rem 0 0.75rem;
18616
+ }
18617
+
18618
+ /* Priority */
18619
+ .priority-high { color: var(--red); }
18620
+ .priority-medium { color: var(--amber); }
18621
+ .priority-low { color: var(--green); }
18622
+ `;
18623
+ }
18624
+
18625
+ // src/web/templates/pages/overview.ts
18626
+ function overviewPage(data) {
18627
+ const cards = data.types.map(
18628
+ (t) => `
18629
+ <div class="card">
18630
+ <a href="/docs/${t.type}">
18631
+ <div class="card-label">${escapeHtml(typeLabel(t.type))}s</div>
18632
+ <div class="card-value">${t.total}</div>
18633
+ ${t.open > 0 ? `<div class="card-sub">${t.open} open</div>` : `<div class="card-sub">none open</div>`}
18634
+ </a>
18635
+ </div>`
18636
+ ).join("\n");
18637
+ const rows = data.recent.map(
18638
+ (doc) => `
18639
+ <tr>
18640
+ <td><a href="/docs/${doc.frontmatter.type}/${doc.frontmatter.id}">${escapeHtml(doc.frontmatter.id)}</a></td>
18641
+ <td>${escapeHtml(doc.frontmatter.title)}</td>
18642
+ <td>${escapeHtml(typeLabel(doc.frontmatter.type))}</td>
18643
+ <td>${statusBadge(doc.frontmatter.status)}</td>
18644
+ <td>${formatDate(doc.frontmatter.updated ?? doc.frontmatter.created)}</td>
18645
+ </tr>`
18646
+ ).join("\n");
18647
+ return `
18648
+ <div class="page-header">
18649
+ <h2>Project Overview</h2>
18650
+ </div>
18651
+
18652
+ <div class="cards">
18653
+ ${cards}
18654
+ </div>
18655
+
18656
+ <div class="section-title">Recent Activity</div>
18657
+ ${data.recent.length > 0 ? `
18658
+ <div class="table-wrap">
18659
+ <table>
18660
+ <thead>
18661
+ <tr>
18662
+ <th>ID</th>
18663
+ <th>Title</th>
18664
+ <th>Type</th>
18665
+ <th>Status</th>
18666
+ <th>Updated</th>
18667
+ </tr>
18668
+ </thead>
18669
+ <tbody>
18670
+ ${rows}
18671
+ </tbody>
18672
+ </table>
18673
+ </div>` : `<div class="empty"><p>No documents yet.</p></div>`}
18674
+ `;
18675
+ }
18676
+
18677
+ // src/web/templates/pages/documents.ts
18678
+ function documentsPage(data) {
18679
+ const label = typeLabel(data.type);
18680
+ const statusOptions = data.statuses.map(
18681
+ (s) => `<option value="${escapeHtml(s)}"${data.filterStatus === s ? " selected" : ""}>${escapeHtml(s)}</option>`
18682
+ ).join("");
18683
+ const ownerOptions = data.owners.map(
18684
+ (o) => `<option value="${escapeHtml(o)}"${data.filterOwner === o ? " selected" : ""}>${escapeHtml(o)}</option>`
18685
+ ).join("");
18686
+ const rows = data.docs.map(
18687
+ (doc) => `
18688
+ <tr>
18689
+ <td><a href="/docs/${data.type}/${doc.frontmatter.id}">${escapeHtml(doc.frontmatter.id)}</a></td>
18690
+ <td><a href="/docs/${data.type}/${doc.frontmatter.id}">${escapeHtml(doc.frontmatter.title)}</a></td>
18691
+ <td>${statusBadge(doc.frontmatter.status)}</td>
18692
+ <td>${escapeHtml(doc.frontmatter.owner ?? "\u2014")}</td>
18693
+ <td>${doc.frontmatter.priority ? `<span class="priority-${doc.frontmatter.priority.toLowerCase()}">${escapeHtml(doc.frontmatter.priority)}</span>` : "\u2014"}</td>
18694
+ <td>${formatDate(doc.frontmatter.updated ?? doc.frontmatter.created)}</td>
18695
+ </tr>`
18696
+ ).join("\n");
18697
+ return `
18698
+ <div class="page-header">
18699
+ <h2>${escapeHtml(label)}s</h2>
18700
+ <div class="subtitle">${data.docs.length} document${data.docs.length !== 1 ? "s" : ""}</div>
18701
+ </div>
18702
+
18703
+ <div class="filters">
18704
+ <select onchange="filterByStatus(this.value)">
18705
+ <option value="">All statuses</option>
18706
+ ${statusOptions}
18707
+ </select>
18708
+ <select onchange="filterByOwner(this.value)">
18709
+ <option value="">All owners</option>
18710
+ ${ownerOptions}
18711
+ </select>
18712
+ </div>
18713
+
18714
+ ${data.docs.length > 0 ? `
18715
+ <div class="table-wrap">
18716
+ <table>
18717
+ <thead>
18718
+ <tr>
18719
+ <th>ID</th>
18720
+ <th>Title</th>
18721
+ <th>Status</th>
18722
+ <th>Owner</th>
18723
+ <th>Priority</th>
18724
+ <th>Updated</th>
18725
+ </tr>
18726
+ </thead>
18727
+ <tbody>
18728
+ ${rows}
18729
+ </tbody>
18730
+ </table>
18731
+ </div>` : `<div class="empty"><p>No ${label.toLowerCase()}s found.</p></div>`}
18732
+
18733
+ <script>
18734
+ function filterByStatus(status) {
18735
+ const url = new URL(window.location);
18736
+ if (status) url.searchParams.set('status', status);
18737
+ else url.searchParams.delete('status');
18738
+ window.location = url;
18739
+ }
18740
+ function filterByOwner(owner) {
18741
+ const url = new URL(window.location);
18742
+ if (owner) url.searchParams.set('owner', owner);
18743
+ else url.searchParams.delete('owner');
18744
+ window.location = url;
18745
+ }
18746
+ </script>
18747
+ `;
18748
+ }
18749
+
18750
+ // src/web/templates/pages/document-detail.ts
18751
+ function documentDetailPage(doc) {
18752
+ const fm = doc.frontmatter;
18753
+ const label = typeLabel(fm.type);
18754
+ const skipKeys = /* @__PURE__ */ new Set(["title", "type"]);
18755
+ const entries = Object.entries(fm).filter(
18756
+ ([key]) => !skipKeys.has(key) && fm[key] != null
18757
+ );
18758
+ const dtDd = entries.map(([key, value]) => {
18759
+ let rendered;
18760
+ if (key === "status") {
18761
+ rendered = statusBadge(value);
18762
+ } else if (key === "tags" && Array.isArray(value)) {
18763
+ rendered = value.map((t) => `<span class="badge badge-default">${escapeHtml(t)}</span>`).join(" ");
18764
+ } else if (key === "created" || key === "updated") {
18765
+ rendered = formatDate(value);
18766
+ } else {
18767
+ rendered = escapeHtml(String(value));
18768
+ }
18769
+ return `<dt>${escapeHtml(key)}</dt><dd>${rendered}</dd>`;
18770
+ }).join("\n ");
18771
+ return `
18772
+ <div class="breadcrumb">
18773
+ <a href="/">Overview</a><span class="sep">/</span>
18774
+ <a href="/docs/${fm.type}">${escapeHtml(label)}s</a><span class="sep">/</span>
18775
+ ${escapeHtml(fm.id)}
18776
+ </div>
18777
+
18778
+ <div class="page-header">
18779
+ <h2>${escapeHtml(fm.title)}</h2>
18780
+ <div class="subtitle">${escapeHtml(fm.id)} &middot; ${escapeHtml(label)}</div>
18781
+ </div>
18782
+
18783
+ <div class="detail-meta">
18784
+ <dl>
18785
+ ${dtDd}
18786
+ </dl>
18787
+ </div>
18788
+
18789
+ ${doc.content.trim() ? `<div class="detail-content">${renderMarkdown(doc.content)}</div>` : ""}
18790
+ `;
18791
+ }
18792
+
18793
+ // src/web/templates/pages/gar.ts
18794
+ function garPage(report) {
18795
+ const dotClass = `dot-${report.overall}`;
18796
+ const areaCards = report.areas.map(
18797
+ (area) => `
18798
+ <div class="gar-area">
18799
+ <div class="area-header">
18800
+ <div class="area-dot dot-${area.status}"></div>
18801
+ <div class="area-name">${escapeHtml(area.name)}</div>
18802
+ </div>
18803
+ <div class="area-summary">${escapeHtml(area.summary)}</div>
18804
+ ${area.items.length > 0 ? `<ul>${area.items.map((item) => `<li><span class="ref-id">${escapeHtml(item.id)}</span>${escapeHtml(item.title)}</li>`).join("")}</ul>` : ""}
18805
+ </div>`
18806
+ ).join("\n");
18807
+ return `
18808
+ <div class="page-header">
18809
+ <h2>GAR Report</h2>
18810
+ <div class="subtitle">Generated ${escapeHtml(report.generatedAt)}</div>
18811
+ </div>
18812
+
18813
+ <div class="gar-overall">
18814
+ <div class="dot ${dotClass}"></div>
18815
+ <div class="label">Overall: ${escapeHtml(report.overall)}</div>
18816
+ </div>
18817
+
18818
+ <div class="gar-areas">
18819
+ ${areaCards}
18820
+ </div>
18821
+ `;
18822
+ }
18823
+
18824
+ // src/web/templates/pages/board.ts
18825
+ function boardPage(data) {
18826
+ const typeOptions = data.types.map(
18827
+ (t) => `<option value="${escapeHtml(t)}"${data.type === t ? " selected" : ""}>${escapeHtml(typeLabel(t))}s</option>`
18828
+ ).join("");
18829
+ const columns = data.columns.map(
18830
+ (col) => `
18831
+ <div class="board-column">
18832
+ <div class="board-column-header">
18833
+ <span>${escapeHtml(col.status)}</span>
18834
+ <span class="count">${col.docs.length}</span>
18835
+ </div>
18836
+ ${col.docs.map(
18837
+ (doc) => `
18838
+ <div class="board-card">
18839
+ <a href="/docs/${doc.frontmatter.type}/${doc.frontmatter.id}">
18840
+ <div class="bc-id">${escapeHtml(doc.frontmatter.id)}</div>
18841
+ <div class="bc-title">${escapeHtml(doc.frontmatter.title)}</div>
18842
+ ${doc.frontmatter.owner ? `<div class="bc-owner">${escapeHtml(doc.frontmatter.owner)}</div>` : ""}
18843
+ </a>
18844
+ </div>`
18845
+ ).join("\n")}
18846
+ </div>`
18847
+ ).join("\n");
18848
+ return `
18849
+ <div class="page-header">
18850
+ <h2>Status Board</h2>
18851
+ </div>
18852
+
18853
+ <div class="filters">
18854
+ <select onchange="filterByType(this.value)">
18855
+ <option value="">All types</option>
18856
+ ${typeOptions}
18857
+ </select>
18858
+ </div>
18859
+
18860
+ ${data.columns.length > 0 ? `<div class="board">${columns}</div>` : `<div class="empty"><p>No documents to display.</p></div>`}
18861
+
18862
+ <script>
18863
+ function filterByType(type) {
18864
+ if (type) window.location = '/board/' + type;
18865
+ else window.location = '/board';
18866
+ }
18867
+ </script>
18868
+ `;
18869
+ }
18870
+
18871
+ // src/web/router.ts
18872
+ function handleRequest(req, res, store, projectName, navGroups) {
18873
+ const parsed = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
18874
+ const pathname = parsed.pathname;
18875
+ const navTypes = store.registeredTypes;
18876
+ try {
18877
+ if (pathname === "/styles.css") {
18878
+ res.writeHead(200, {
18879
+ "Content-Type": "text/css",
18880
+ "Cache-Control": "public, max-age=300"
18881
+ });
18882
+ res.end(renderStyles());
18883
+ return;
18884
+ }
18885
+ if (pathname === "/") {
18886
+ const data = getOverviewData(store);
18887
+ const body = overviewPage(data);
18888
+ respond(res, layout({ title: "Overview", activePath: "/", projectName, navGroups }, body));
18889
+ return;
18890
+ }
18891
+ if (pathname === "/gar") {
18892
+ const report = getGarData(store, projectName);
18893
+ const body = garPage(report);
18894
+ respond(res, layout({ title: "GAR Report", activePath: "/gar", projectName, navGroups }, body));
18895
+ return;
18896
+ }
18897
+ const boardMatch = pathname.match(/^\/board(?:\/([^/]+))?$/);
18898
+ if (boardMatch) {
18899
+ const type = boardMatch[1];
18900
+ if (type && !navTypes.includes(type)) {
18901
+ notFound(res, projectName, navGroups, pathname);
18902
+ return;
18903
+ }
18904
+ const data = getBoardData(store, type);
18905
+ const body = boardPage(data);
18906
+ respond(res, layout({ title: "Board", activePath: "/board", projectName, navGroups }, body));
18907
+ return;
18908
+ }
18909
+ const detailMatch = pathname.match(/^\/docs\/([^/]+)\/([^/]+)$/);
18910
+ if (detailMatch) {
18911
+ const [, type, id] = detailMatch;
18912
+ const doc = getDocumentDetail(store, type, id);
18913
+ if (!doc) {
18914
+ notFound(res, projectName, navGroups, pathname);
18915
+ return;
18916
+ }
18917
+ const body = documentDetailPage(doc);
18918
+ respond(res, layout({ title: `${id} \u2014 ${doc.frontmatter.title}`, activePath: `/docs/${type}`, projectName, navGroups }, body));
18919
+ return;
18920
+ }
18921
+ const listMatch = pathname.match(/^\/docs\/([^/]+)$/);
18922
+ if (listMatch) {
18923
+ const type = listMatch[1];
18924
+ const filterStatus = parsed.searchParams.get("status") ?? void 0;
18925
+ const filterOwner = parsed.searchParams.get("owner") ?? void 0;
18926
+ const data = getDocumentListData(store, type, filterStatus, filterOwner);
18927
+ if (!data) {
18928
+ notFound(res, projectName, navGroups, pathname);
18929
+ return;
18930
+ }
18931
+ const body = documentsPage(data);
18932
+ respond(res, layout({ title: `${type}`, activePath: `/docs/${type}`, projectName, navGroups }, body));
18933
+ return;
18934
+ }
18935
+ notFound(res, projectName, navGroups, pathname);
18936
+ } catch (err) {
18937
+ console.error("[marvin web] Error handling request:", err);
18938
+ res.writeHead(500, { "Content-Type": "text/html" });
18939
+ res.end("<h1>500 \u2014 Internal Server Error</h1>");
18940
+ }
18941
+ }
18942
+ function respond(res, html) {
18943
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
18944
+ res.end(html);
18945
+ }
18946
+ function notFound(res, projectName, navGroups, activePath) {
18947
+ const body = `<div class="empty"><h2>404</h2><p>Page not found.</p><p><a href="/">Go to overview</a></p></div>`;
18948
+ res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
18949
+ res.end(layout({ title: "Not Found", activePath, projectName, navGroups }, body));
18950
+ }
18951
+
18952
+ // src/web/server.ts
18953
+ import * as http from "http";
18954
+ import { exec } from "child_process";
18955
+ function openBrowser(url2) {
18956
+ const platform = process.platform;
18957
+ const cmd = platform === "darwin" ? `open "${url2}"` : platform === "win32" ? `start "${url2}"` : `xdg-open "${url2}"`;
18958
+ exec(cmd, (err) => {
18959
+ if (err) {
18960
+ }
18961
+ });
18962
+ }
18963
+
18964
+ // src/agent/tools/web.ts
18965
+ var runningServer = null;
18966
+ function createWebTools(store, projectName, navGroups) {
18967
+ return [
18968
+ tool20(
18969
+ "start_web_dashboard",
18970
+ "Start the Marvin web dashboard on a local port. Returns the base URL. If already running, returns the existing URL.",
18971
+ {
18972
+ port: external_exports.number().optional().describe("Port to listen on (default: 3000)"),
18973
+ open: external_exports.boolean().optional().describe("Open the dashboard in the default browser (default: true)")
18974
+ },
18975
+ async (args) => {
18976
+ const port = args.port ?? 3e3;
18977
+ if (runningServer) {
18978
+ const url3 = `http://localhost:${runningServer.port}`;
18979
+ return {
18980
+ content: [{ type: "text", text: `Dashboard already running at ${url3}` }]
18981
+ };
18982
+ }
18983
+ const server = http2.createServer((req, res) => {
18984
+ handleRequest(req, res, store, projectName, navGroups);
18985
+ });
18986
+ await new Promise((resolve3, reject) => {
18987
+ server.on("error", reject);
18988
+ server.listen(port, () => resolve3());
18989
+ });
18990
+ runningServer = { server, port };
18991
+ const url2 = `http://localhost:${port}`;
18992
+ if (args.open !== false) {
18993
+ openBrowser(url2);
18994
+ }
18995
+ return {
18996
+ content: [{ type: "text", text: `Dashboard started at ${url2}` }]
18997
+ };
18998
+ }
18999
+ ),
19000
+ tool20(
19001
+ "stop_web_dashboard",
19002
+ "Stop the running Marvin web dashboard.",
19003
+ {},
19004
+ async () => {
19005
+ if (!runningServer) {
19006
+ return {
19007
+ content: [{ type: "text", text: "No dashboard is currently running." }],
19008
+ isError: true
19009
+ };
19010
+ }
19011
+ await new Promise((resolve3) => {
19012
+ runningServer.server.close(() => resolve3());
19013
+ });
19014
+ runningServer = null;
19015
+ return {
19016
+ content: [{ type: "text", text: "Dashboard stopped." }]
19017
+ };
19018
+ }
19019
+ ),
19020
+ tool20(
19021
+ "get_web_dashboard_urls",
19022
+ "Get all available dashboard page URLs. The dashboard must be running.",
19023
+ {},
19024
+ async () => {
19025
+ if (!runningServer) {
19026
+ return {
19027
+ content: [{ type: "text", text: "Dashboard is not running. Use start_web_dashboard first." }],
19028
+ isError: true
19029
+ };
19030
+ }
19031
+ const base = `http://localhost:${runningServer.port}`;
19032
+ const urls = {
19033
+ overview: base,
19034
+ gar: `${base}/gar`,
19035
+ board: `${base}/board`
19036
+ };
19037
+ for (const type of store.registeredTypes) {
19038
+ urls[type] = `${base}/docs/${type}`;
19039
+ }
19040
+ return {
19041
+ content: [{ type: "text", text: JSON.stringify(urls, null, 2) }]
19042
+ };
19043
+ },
19044
+ { annotations: { readOnly: true } }
19045
+ ),
19046
+ tool20(
19047
+ "get_dashboard_overview",
19048
+ "Get the project overview data: document type counts and recent activity. Works without the web server running.",
19049
+ {},
19050
+ async () => {
19051
+ const data = getOverviewData(store);
19052
+ const result = {
19053
+ types: data.types,
19054
+ recent: data.recent.map((d) => ({
19055
+ id: d.frontmatter.id,
19056
+ type: d.frontmatter.type,
19057
+ title: d.frontmatter.title,
19058
+ status: d.frontmatter.status,
19059
+ updated: d.frontmatter.updated ?? d.frontmatter.created
19060
+ }))
19061
+ };
19062
+ return {
19063
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
19064
+ };
19065
+ },
19066
+ { annotations: { readOnly: true } }
19067
+ ),
19068
+ tool20(
19069
+ "get_dashboard_gar",
19070
+ "Get the GAR (Governance, Actions, Risks) report as JSON. Works without the web server running.",
19071
+ {},
19072
+ async () => {
19073
+ const report = getGarData(store, projectName);
19074
+ return {
19075
+ content: [{ type: "text", text: JSON.stringify(report, null, 2) }]
19076
+ };
19077
+ },
19078
+ { annotations: { readOnly: true } }
19079
+ ),
19080
+ tool20(
19081
+ "get_dashboard_board",
19082
+ "Get board data showing documents grouped by status. Optionally filter by document type. Works without the web server running.",
19083
+ {
19084
+ type: external_exports.string().optional().describe("Document type to filter by (e.g. 'decision', 'action')")
19085
+ },
19086
+ async (args) => {
19087
+ const data = getBoardData(store, args.type);
19088
+ const result = {
19089
+ type: data.type ?? "all",
19090
+ types: data.types,
19091
+ columns: data.columns.map((col) => ({
19092
+ status: col.status,
19093
+ count: col.docs.length,
19094
+ docs: col.docs.map((d) => ({
19095
+ id: d.frontmatter.id,
19096
+ type: d.frontmatter.type,
19097
+ title: d.frontmatter.title,
19098
+ owner: d.frontmatter.owner
19099
+ }))
19100
+ }))
19101
+ };
19102
+ return {
19103
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
19104
+ };
19105
+ },
19106
+ { annotations: { readOnly: true } }
19107
+ )
19108
+ ];
19109
+ }
19110
+
19111
+ // src/agent/mcp-server.ts
17946
19112
  function createMarvinMcpServer(store, options) {
17947
19113
  const tools = [
17948
19114
  ...createDecisionTools(store),
@@ -17952,7 +19118,8 @@ function createMarvinMcpServer(store, options) {
17952
19118
  ...options?.manifest ? createSourceTools(options.manifest) : [],
17953
19119
  ...options?.sessionStore ? createSessionTools(options.sessionStore) : [],
17954
19120
  ...options?.pluginTools ?? [],
17955
- ...options?.skillTools ?? []
19121
+ ...options?.skillTools ?? [],
19122
+ ...options?.projectName && options?.navGroups ? createWebTools(store, options.projectName, options.navGroups) : []
17956
19123
  ];
17957
19124
  return createSdkMcpServer({
17958
19125
  name: "marvin-governance",
@@ -18024,7 +19191,7 @@ function createSkillActionTools(skills, context) {
18024
19191
  if (!skill.actions) continue;
18025
19192
  for (const action of skill.actions) {
18026
19193
  tools.push(
18027
- tool20(
19194
+ tool21(
18028
19195
  `${skill.id}__${action.id}`,
18029
19196
  action.description,
18030
19197
  {
@@ -18251,10 +19418,10 @@ ${lines.join("\n\n")}`;
18251
19418
  }
18252
19419
 
18253
19420
  // src/mcp/persona-tools.ts
18254
- import { tool as tool21 } from "@anthropic-ai/claude-agent-sdk";
19421
+ import { tool as tool22 } from "@anthropic-ai/claude-agent-sdk";
18255
19422
  function createPersonaTools(ctx, marvinDir) {
18256
19423
  return [
18257
- tool21(
19424
+ tool22(
18258
19425
  "set_persona",
18259
19426
  "Set the active persona for this session. Returns full guidance for the selected persona including behavioral rules, allowed document types, and scope. Call this before working to ensure persona-appropriate behavior.",
18260
19427
  {
@@ -18284,7 +19451,7 @@ ${summaries}`
18284
19451
  };
18285
19452
  }
18286
19453
  ),
18287
- tool21(
19454
+ tool22(
18288
19455
  "get_persona_guidance",
18289
19456
  "Get guidance for a persona without changing the active persona. If no persona is specified, lists all available personas with summaries.",
18290
19457
  {