mrvn-cli 0.3.1 → 0.3.2
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 +60 -21
- package/dist/index.js.map +1 -1
- package/dist/marvin.js +60 -21
- package/dist/marvin.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21572,16 +21572,23 @@ function inline(text) {
|
|
|
21572
21572
|
return s;
|
|
21573
21573
|
}
|
|
21574
21574
|
function layout(opts, body) {
|
|
21575
|
-
const
|
|
21575
|
+
const topItems = [
|
|
21576
21576
|
{ href: "/", label: "Overview" },
|
|
21577
21577
|
{ href: "/board", label: "Board" },
|
|
21578
21578
|
{ href: "/gar", label: "GAR Report" }
|
|
21579
21579
|
];
|
|
21580
|
-
const typeNavItems = opts.navTypes.map((type) => ({
|
|
21581
|
-
href: `/docs/${type}`,
|
|
21582
|
-
label: typeLabel(type) + "s"
|
|
21583
|
-
}));
|
|
21584
21580
|
const isActive = (href) => opts.activePath === href || href !== "/" && opts.activePath.startsWith(href) ? " active" : "";
|
|
21581
|
+
const groupsHtml = opts.navGroups.map((group) => {
|
|
21582
|
+
const links = group.types.map((type) => {
|
|
21583
|
+
const href = `/docs/${type}`;
|
|
21584
|
+
return `<a href="${href}" class="${isActive(href)}">${typeLabel(type)}s</a>`;
|
|
21585
|
+
}).join("\n ");
|
|
21586
|
+
return `
|
|
21587
|
+
<div class="nav-group">
|
|
21588
|
+
<div class="nav-group-label">${escapeHtml(group.label)}</div>
|
|
21589
|
+
${links}
|
|
21590
|
+
</div>`;
|
|
21591
|
+
}).join("\n");
|
|
21585
21592
|
return `<!DOCTYPE html>
|
|
21586
21593
|
<html lang="en">
|
|
21587
21594
|
<head>
|
|
@@ -21598,8 +21605,8 @@ function layout(opts, body) {
|
|
|
21598
21605
|
<div class="project-name">${escapeHtml(opts.projectName)}</div>
|
|
21599
21606
|
</div>
|
|
21600
21607
|
<nav>
|
|
21601
|
-
${
|
|
21602
|
-
${
|
|
21608
|
+
${topItems.map((n) => `<a href="${n.href}" class="${isActive(n.href)}">${n.label}</a>`).join("\n ")}
|
|
21609
|
+
${groupsHtml}
|
|
21603
21610
|
</nav>
|
|
21604
21611
|
</aside>
|
|
21605
21612
|
<main class="main">
|
|
@@ -21700,6 +21707,21 @@ a:hover { text-decoration: underline; }
|
|
|
21700
21707
|
border-right: 2px solid var(--accent);
|
|
21701
21708
|
}
|
|
21702
21709
|
|
|
21710
|
+
.nav-group {
|
|
21711
|
+
margin-top: 0.75rem;
|
|
21712
|
+
padding-top: 0.75rem;
|
|
21713
|
+
border-top: 1px solid var(--border);
|
|
21714
|
+
}
|
|
21715
|
+
|
|
21716
|
+
.nav-group-label {
|
|
21717
|
+
padding: 0.25rem 1.25rem 0.25rem;
|
|
21718
|
+
font-size: 0.65rem;
|
|
21719
|
+
text-transform: uppercase;
|
|
21720
|
+
letter-spacing: 0.08em;
|
|
21721
|
+
color: var(--text-dim);
|
|
21722
|
+
font-weight: 600;
|
|
21723
|
+
}
|
|
21724
|
+
|
|
21703
21725
|
.main {
|
|
21704
21726
|
margin-left: 220px;
|
|
21705
21727
|
flex: 1;
|
|
@@ -22314,7 +22336,7 @@ function boardPage(data) {
|
|
|
22314
22336
|
}
|
|
22315
22337
|
|
|
22316
22338
|
// src/web/router.ts
|
|
22317
|
-
function handleRequest(req, res, store, projectName) {
|
|
22339
|
+
function handleRequest(req, res, store, projectName, navGroups) {
|
|
22318
22340
|
const parsed = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
22319
22341
|
const pathname = parsed.pathname;
|
|
22320
22342
|
const navTypes = store.registeredTypes;
|
|
@@ -22330,25 +22352,25 @@ function handleRequest(req, res, store, projectName) {
|
|
|
22330
22352
|
if (pathname === "/") {
|
|
22331
22353
|
const data = getOverviewData(store);
|
|
22332
22354
|
const body = overviewPage(data);
|
|
22333
|
-
respond(res, layout({ title: "Overview", activePath: "/", projectName,
|
|
22355
|
+
respond(res, layout({ title: "Overview", activePath: "/", projectName, navGroups }, body));
|
|
22334
22356
|
return;
|
|
22335
22357
|
}
|
|
22336
22358
|
if (pathname === "/gar") {
|
|
22337
22359
|
const report = getGarData(store, projectName);
|
|
22338
22360
|
const body = garPage(report);
|
|
22339
|
-
respond(res, layout({ title: "GAR Report", activePath: "/gar", projectName,
|
|
22361
|
+
respond(res, layout({ title: "GAR Report", activePath: "/gar", projectName, navGroups }, body));
|
|
22340
22362
|
return;
|
|
22341
22363
|
}
|
|
22342
22364
|
const boardMatch = pathname.match(/^\/board(?:\/([^/]+))?$/);
|
|
22343
22365
|
if (boardMatch) {
|
|
22344
22366
|
const type = boardMatch[1];
|
|
22345
22367
|
if (type && !navTypes.includes(type)) {
|
|
22346
|
-
notFound(res, projectName,
|
|
22368
|
+
notFound(res, projectName, navGroups, pathname);
|
|
22347
22369
|
return;
|
|
22348
22370
|
}
|
|
22349
22371
|
const data = getBoardData(store, type);
|
|
22350
22372
|
const body = boardPage(data);
|
|
22351
|
-
respond(res, layout({ title: "Board", activePath: "/board", projectName,
|
|
22373
|
+
respond(res, layout({ title: "Board", activePath: "/board", projectName, navGroups }, body));
|
|
22352
22374
|
return;
|
|
22353
22375
|
}
|
|
22354
22376
|
const detailMatch = pathname.match(/^\/docs\/([^/]+)\/([^/]+)$/);
|
|
@@ -22356,11 +22378,11 @@ function handleRequest(req, res, store, projectName) {
|
|
|
22356
22378
|
const [, type, id] = detailMatch;
|
|
22357
22379
|
const doc = getDocumentDetail(store, type, id);
|
|
22358
22380
|
if (!doc) {
|
|
22359
|
-
notFound(res, projectName,
|
|
22381
|
+
notFound(res, projectName, navGroups, pathname);
|
|
22360
22382
|
return;
|
|
22361
22383
|
}
|
|
22362
22384
|
const body = documentDetailPage(doc);
|
|
22363
|
-
respond(res, layout({ title: `${id} \u2014 ${doc.frontmatter.title}`, activePath: `/docs/${type}`, projectName,
|
|
22385
|
+
respond(res, layout({ title: `${id} \u2014 ${doc.frontmatter.title}`, activePath: `/docs/${type}`, projectName, navGroups }, body));
|
|
22364
22386
|
return;
|
|
22365
22387
|
}
|
|
22366
22388
|
const listMatch = pathname.match(/^\/docs\/([^/]+)$/);
|
|
@@ -22370,14 +22392,14 @@ function handleRequest(req, res, store, projectName) {
|
|
|
22370
22392
|
const filterOwner = parsed.searchParams.get("owner") ?? void 0;
|
|
22371
22393
|
const data = getDocumentListData(store, type, filterStatus, filterOwner);
|
|
22372
22394
|
if (!data) {
|
|
22373
|
-
notFound(res, projectName,
|
|
22395
|
+
notFound(res, projectName, navGroups, pathname);
|
|
22374
22396
|
return;
|
|
22375
22397
|
}
|
|
22376
22398
|
const body = documentsPage(data);
|
|
22377
|
-
respond(res, layout({ title: `${type}`, activePath: `/docs/${type}`, projectName,
|
|
22399
|
+
respond(res, layout({ title: `${type}`, activePath: `/docs/${type}`, projectName, navGroups }, body));
|
|
22378
22400
|
return;
|
|
22379
22401
|
}
|
|
22380
|
-
notFound(res, projectName,
|
|
22402
|
+
notFound(res, projectName, navGroups, pathname);
|
|
22381
22403
|
} catch (err) {
|
|
22382
22404
|
console.error("[marvin web] Error handling request:", err);
|
|
22383
22405
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
@@ -22388,13 +22410,14 @@ function respond(res, html) {
|
|
|
22388
22410
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
22389
22411
|
res.end(html);
|
|
22390
22412
|
}
|
|
22391
|
-
function notFound(res, projectName,
|
|
22413
|
+
function notFound(res, projectName, navGroups, activePath) {
|
|
22392
22414
|
const body = `<div class="empty"><h2>404</h2><p>Page not found.</p><p><a href="/">Go to overview</a></p></div>`;
|
|
22393
22415
|
res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
|
|
22394
|
-
res.end(layout({ title: "Not Found", activePath, projectName,
|
|
22416
|
+
res.end(layout({ title: "Not Found", activePath, projectName, navGroups }, body));
|
|
22395
22417
|
}
|
|
22396
22418
|
|
|
22397
22419
|
// src/web/server.ts
|
|
22420
|
+
var CORE_TYPES = ["decision", "action", "question"];
|
|
22398
22421
|
async function startWebServer(opts) {
|
|
22399
22422
|
const project = loadProject();
|
|
22400
22423
|
const plugin = resolvePlugin(project.config.methodology);
|
|
@@ -22407,8 +22430,24 @@ async function startWebServer(opts) {
|
|
|
22407
22430
|
...skillRegs
|
|
22408
22431
|
]);
|
|
22409
22432
|
const projectName = project.config.name;
|
|
22433
|
+
const commonTypes = new Set(COMMON_REGISTRATIONS.map((r) => r.type));
|
|
22434
|
+
const pluginOnlyTypes = pluginRegs.map((r) => r.type).filter((t) => !commonTypes.has(t));
|
|
22435
|
+
const skillTypes = skillRegs.map((r) => r.type);
|
|
22436
|
+
const navGroups = [
|
|
22437
|
+
{ label: "Governance", types: CORE_TYPES },
|
|
22438
|
+
{ label: "Project", types: [...commonTypes] }
|
|
22439
|
+
];
|
|
22440
|
+
if (pluginOnlyTypes.length > 0) {
|
|
22441
|
+
navGroups.push({
|
|
22442
|
+
label: plugin?.name ?? "Plugin",
|
|
22443
|
+
types: pluginOnlyTypes
|
|
22444
|
+
});
|
|
22445
|
+
}
|
|
22446
|
+
if (skillTypes.length > 0) {
|
|
22447
|
+
navGroups.push({ label: "Skills", types: skillTypes });
|
|
22448
|
+
}
|
|
22410
22449
|
const server = http.createServer((req, res) => {
|
|
22411
|
-
handleRequest(req, res, store, projectName);
|
|
22450
|
+
handleRequest(req, res, store, projectName, navGroups);
|
|
22412
22451
|
});
|
|
22413
22452
|
server.listen(opts.port, () => {
|
|
22414
22453
|
const url2 = `http://localhost:${opts.port}`;
|
|
@@ -22452,7 +22491,7 @@ function createProgram() {
|
|
|
22452
22491
|
const program = new Command();
|
|
22453
22492
|
program.name("marvin").description(
|
|
22454
22493
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
22455
|
-
).version("0.3.
|
|
22494
|
+
).version("0.3.2");
|
|
22456
22495
|
program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
22457
22496
|
await initCommand();
|
|
22458
22497
|
});
|