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