mrvn-cli 0.2.0 → 0.2.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 +221 -11
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +379 -10
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +221 -11
- package/dist/marvin.js.map +1 -1
- package/package.json +1 -1
package/dist/marvin.js
CHANGED
|
@@ -13981,7 +13981,7 @@ function createMeetingTools(store) {
|
|
|
13981
13981
|
owner: external_exports.string().optional().describe("Meeting organizer"),
|
|
13982
13982
|
tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization"),
|
|
13983
13983
|
attendees: external_exports.array(external_exports.string()).optional().describe("List of attendees"),
|
|
13984
|
-
date: external_exports.string().
|
|
13984
|
+
date: external_exports.string().describe("Date the meeting took place (ISO format, e.g. '2025-01-15'). Extract from the meeting content. If not found, ask the user before calling this tool.")
|
|
13985
13985
|
},
|
|
13986
13986
|
async (args) => {
|
|
13987
13987
|
const frontmatter = {
|
|
@@ -13991,7 +13991,7 @@ function createMeetingTools(store) {
|
|
|
13991
13991
|
if (args.owner) frontmatter.owner = args.owner;
|
|
13992
13992
|
if (args.tags) frontmatter.tags = args.tags;
|
|
13993
13993
|
if (args.attendees) frontmatter.attendees = args.attendees;
|
|
13994
|
-
|
|
13994
|
+
frontmatter.date = args.date;
|
|
13995
13995
|
const doc = store.create(
|
|
13996
13996
|
"meeting",
|
|
13997
13997
|
frontmatter,
|
|
@@ -14691,7 +14691,7 @@ var genericAgilePlugin = {
|
|
|
14691
14691
|
|
|
14692
14692
|
**Meeting Tools:**
|
|
14693
14693
|
- **list_meetings** / **get_meeting**: Browse and read meeting records.
|
|
14694
|
-
- **create_meeting**: Record new meetings with attendees, date, and agenda.
|
|
14694
|
+
- **create_meeting**: Record new meetings with attendees, date, and agenda. The meeting date is required \u2014 extract it from the meeting content or ask the user if not found.
|
|
14695
14695
|
- **update_meeting**: Update meeting status or notes after completion.
|
|
14696
14696
|
- **analyze_meeting**: Analyze a meeting to review its outcomes and extract artifacts.
|
|
14697
14697
|
|
|
@@ -14712,7 +14712,7 @@ var genericAgilePlugin = {
|
|
|
14712
14712
|
|
|
14713
14713
|
**Meeting Tools:**
|
|
14714
14714
|
- **list_meetings** / **get_meeting**: Browse and read meeting records.
|
|
14715
|
-
- **create_meeting**: Record new meetings with attendees, date, and agenda.
|
|
14715
|
+
- **create_meeting**: Record new meetings with attendees, date, and agenda. The meeting date is required \u2014 extract it from the meeting content or ask the user if not found.
|
|
14716
14716
|
- **update_meeting**: Update meeting status or notes after completion.
|
|
14717
14717
|
- **analyze_meeting**: Analyze a meeting to review its outcomes and extract artifacts.
|
|
14718
14718
|
|
|
@@ -14740,7 +14740,7 @@ var genericAgilePlugin = {
|
|
|
14740
14740
|
|
|
14741
14741
|
**Meeting Tools:**
|
|
14742
14742
|
- **list_meetings** / **get_meeting**: Browse and read meeting records.
|
|
14743
|
-
- **create_meeting**: Record new meetings with attendees, date, and agenda.
|
|
14743
|
+
- **create_meeting**: Record new meetings with attendees, date, and agenda. The meeting date is required \u2014 extract it from the meeting content or ask the user if not found.
|
|
14744
14744
|
- **update_meeting**: Update meeting status or notes after completion.
|
|
14745
14745
|
- **analyze_meeting**: Analyze a completed meeting to extract decisions, actions, and questions. Use this to ensure meeting outcomes are properly tracked as governance artifacts.
|
|
14746
14746
|
|
|
@@ -14758,7 +14758,7 @@ var genericAgilePlugin = {
|
|
|
14758
14758
|
**Key workflow rule:** Epics must link to approved features \u2014 the system enforces this. The Product Owner defines and approves features, the Tech Lead breaks them into epics, and the Delivery Manager tracks dates and progress.
|
|
14759
14759
|
|
|
14760
14760
|
- **list_meetings** / **get_meeting**: Browse and read meeting records.
|
|
14761
|
-
- **create_meeting**: Record meetings with attendees, date, and agenda.
|
|
14761
|
+
- **create_meeting**: Record meetings with attendees, date, and agenda. The meeting date is required \u2014 extract it from the meeting content or ask the user if not found.
|
|
14762
14762
|
- **update_meeting**: Update meeting status or notes.
|
|
14763
14763
|
- **analyze_meeting**: Analyze a meeting to extract decisions, actions, and questions as governance artifacts.`
|
|
14764
14764
|
}
|
|
@@ -16103,7 +16103,7 @@ var DocumentStore = class {
|
|
|
16103
16103
|
updated: now,
|
|
16104
16104
|
...cleaned
|
|
16105
16105
|
};
|
|
16106
|
-
const fileName = type === "meeting" ? `${now.slice(0, 10)}-${slugify2(fullFrontmatter.title)}.md` : `${id}.md`;
|
|
16106
|
+
const fileName = type === "meeting" ? `${cleaned.date?.slice(0, 10) ?? now.slice(0, 10)}-${slugify2(fullFrontmatter.title)}.md` : `${id}.md`;
|
|
16107
16107
|
const filePath = path5.join(dir, fileName);
|
|
16108
16108
|
const doc = {
|
|
16109
16109
|
frontmatter: fullFrontmatter,
|
|
@@ -16127,7 +16127,7 @@ var DocumentStore = class {
|
|
|
16127
16127
|
}
|
|
16128
16128
|
const dir = path5.join(this.docsDir, dirName);
|
|
16129
16129
|
fs5.mkdirSync(dir, { recursive: true });
|
|
16130
|
-
const fileName = type === "meeting" ? `${frontmatter.created.slice(0, 10)}-${slugify2(frontmatter.title)}.md` : `${frontmatter.id}.md`;
|
|
16130
|
+
const fileName = type === "meeting" ? `${frontmatter.date?.slice(0, 10) ?? frontmatter.created.slice(0, 10)}-${slugify2(frontmatter.title)}.md` : `${frontmatter.id}.md`;
|
|
16131
16131
|
const filePath = path5.join(dir, fileName);
|
|
16132
16132
|
const doc = { frontmatter, content, filePath };
|
|
16133
16133
|
fs5.writeFileSync(filePath, serializeDocument(doc), "utf-8");
|
|
@@ -18458,6 +18458,213 @@ function createSkillActionTools(skills, context) {
|
|
|
18458
18458
|
return tools;
|
|
18459
18459
|
}
|
|
18460
18460
|
|
|
18461
|
+
// src/mcp/persona-context.ts
|
|
18462
|
+
var PersonaContextManager = class {
|
|
18463
|
+
activePersona = null;
|
|
18464
|
+
setPersona(idOrShortName) {
|
|
18465
|
+
const persona = getPersona(idOrShortName);
|
|
18466
|
+
if (persona) {
|
|
18467
|
+
this.activePersona = persona;
|
|
18468
|
+
}
|
|
18469
|
+
return persona;
|
|
18470
|
+
}
|
|
18471
|
+
getActivePersona() {
|
|
18472
|
+
return this.activePersona;
|
|
18473
|
+
}
|
|
18474
|
+
clearPersona() {
|
|
18475
|
+
this.activePersona = null;
|
|
18476
|
+
}
|
|
18477
|
+
isDocumentTypeAllowed(docType) {
|
|
18478
|
+
if (!this.activePersona) return true;
|
|
18479
|
+
return this.activePersona.documentTypes.includes(docType);
|
|
18480
|
+
}
|
|
18481
|
+
};
|
|
18482
|
+
function buildMcpGuidance(persona, marvinDir) {
|
|
18483
|
+
const parts = [];
|
|
18484
|
+
parts.push(`# Active Persona: ${persona.name} (${persona.shortName})`);
|
|
18485
|
+
parts.push(`
|
|
18486
|
+
${persona.description}`);
|
|
18487
|
+
parts.push(`
|
|
18488
|
+
## Focus Areas`);
|
|
18489
|
+
for (const area of persona.focusAreas) {
|
|
18490
|
+
parts.push(`- ${area}`);
|
|
18491
|
+
}
|
|
18492
|
+
parts.push(`
|
|
18493
|
+
## Allowed Document Types`);
|
|
18494
|
+
parts.push(persona.documentTypes.join(", "));
|
|
18495
|
+
parts.push(`
|
|
18496
|
+
## Behavioral Instructions`);
|
|
18497
|
+
parts.push(persona.systemPrompt);
|
|
18498
|
+
try {
|
|
18499
|
+
const config2 = loadProjectConfig(marvinDir);
|
|
18500
|
+
const plugin = resolvePlugin(config2.methodology);
|
|
18501
|
+
if (plugin) {
|
|
18502
|
+
const fragment = getPluginPromptFragment(plugin, persona.id);
|
|
18503
|
+
if (fragment) {
|
|
18504
|
+
parts.push(`
|
|
18505
|
+
## Plugin Rules`);
|
|
18506
|
+
parts.push(fragment);
|
|
18507
|
+
}
|
|
18508
|
+
}
|
|
18509
|
+
const allSkills = loadAllSkills(marvinDir);
|
|
18510
|
+
const skillIds = resolveSkillsForPersona(
|
|
18511
|
+
persona.id,
|
|
18512
|
+
config2.skills,
|
|
18513
|
+
allSkills
|
|
18514
|
+
);
|
|
18515
|
+
if (skillIds.length > 0) {
|
|
18516
|
+
const fragment = getSkillPromptFragment(skillIds, allSkills, persona.id);
|
|
18517
|
+
if (fragment) {
|
|
18518
|
+
parts.push(`
|
|
18519
|
+
## Skill Rules`);
|
|
18520
|
+
parts.push(fragment);
|
|
18521
|
+
}
|
|
18522
|
+
}
|
|
18523
|
+
} catch {
|
|
18524
|
+
}
|
|
18525
|
+
return parts.join("\n");
|
|
18526
|
+
}
|
|
18527
|
+
function buildPersonaSummaries() {
|
|
18528
|
+
const personas = listPersonas();
|
|
18529
|
+
const lines = personas.map(
|
|
18530
|
+
(p) => `- **${p.name}** (${p.shortName}): ${p.description}
|
|
18531
|
+
Document types: ${p.documentTypes.join(", ")}`
|
|
18532
|
+
);
|
|
18533
|
+
return `# Available Personas
|
|
18534
|
+
|
|
18535
|
+
${lines.join("\n\n")}`;
|
|
18536
|
+
}
|
|
18537
|
+
|
|
18538
|
+
// src/mcp/persona-tools.ts
|
|
18539
|
+
import { tool as tool17 } from "@anthropic-ai/claude-agent-sdk";
|
|
18540
|
+
function createPersonaTools(ctx, marvinDir) {
|
|
18541
|
+
return [
|
|
18542
|
+
tool17(
|
|
18543
|
+
"set_persona",
|
|
18544
|
+
"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.",
|
|
18545
|
+
{
|
|
18546
|
+
persona: external_exports.string().describe(
|
|
18547
|
+
'Persona ID or short name (e.g. "po", "product-owner", "dm", "delivery-manager", "tl", "tech-lead")'
|
|
18548
|
+
)
|
|
18549
|
+
},
|
|
18550
|
+
async (args) => {
|
|
18551
|
+
const resolved = ctx.setPersona(args.persona);
|
|
18552
|
+
if (!resolved) {
|
|
18553
|
+
const summaries = buildPersonaSummaries();
|
|
18554
|
+
return {
|
|
18555
|
+
content: [
|
|
18556
|
+
{
|
|
18557
|
+
type: "text",
|
|
18558
|
+
text: `Unknown persona "${args.persona}".
|
|
18559
|
+
|
|
18560
|
+
${summaries}`
|
|
18561
|
+
}
|
|
18562
|
+
],
|
|
18563
|
+
isError: true
|
|
18564
|
+
};
|
|
18565
|
+
}
|
|
18566
|
+
const guidance = buildMcpGuidance(resolved, marvinDir);
|
|
18567
|
+
return {
|
|
18568
|
+
content: [{ type: "text", text: guidance }]
|
|
18569
|
+
};
|
|
18570
|
+
}
|
|
18571
|
+
),
|
|
18572
|
+
tool17(
|
|
18573
|
+
"get_persona_guidance",
|
|
18574
|
+
"Get guidance for a persona without changing the active persona. If no persona is specified, lists all available personas with summaries.",
|
|
18575
|
+
{
|
|
18576
|
+
persona: external_exports.string().optional().describe(
|
|
18577
|
+
"Optional persona ID or short name. Omit to list all personas."
|
|
18578
|
+
)
|
|
18579
|
+
},
|
|
18580
|
+
async (args) => {
|
|
18581
|
+
if (!args.persona) {
|
|
18582
|
+
const summaries = buildPersonaSummaries();
|
|
18583
|
+
return {
|
|
18584
|
+
content: [{ type: "text", text: summaries }]
|
|
18585
|
+
};
|
|
18586
|
+
}
|
|
18587
|
+
const resolved = getPersona(args.persona);
|
|
18588
|
+
if (!resolved) {
|
|
18589
|
+
const summaries = buildPersonaSummaries();
|
|
18590
|
+
return {
|
|
18591
|
+
content: [
|
|
18592
|
+
{
|
|
18593
|
+
type: "text",
|
|
18594
|
+
text: `Unknown persona "${args.persona}".
|
|
18595
|
+
|
|
18596
|
+
${summaries}`
|
|
18597
|
+
}
|
|
18598
|
+
],
|
|
18599
|
+
isError: true
|
|
18600
|
+
};
|
|
18601
|
+
}
|
|
18602
|
+
const guidance = buildMcpGuidance(resolved, marvinDir);
|
|
18603
|
+
return {
|
|
18604
|
+
content: [{ type: "text", text: guidance }]
|
|
18605
|
+
};
|
|
18606
|
+
},
|
|
18607
|
+
{ annotations: { readOnly: true } }
|
|
18608
|
+
)
|
|
18609
|
+
];
|
|
18610
|
+
}
|
|
18611
|
+
|
|
18612
|
+
// src/mcp/tool-wrapper.ts
|
|
18613
|
+
function extractDocType(toolName) {
|
|
18614
|
+
if (toolName === "save_report") return "report";
|
|
18615
|
+
const match = toolName.match(/^(?:create|update)_(\w+)$/);
|
|
18616
|
+
return match ? match[1] : void 0;
|
|
18617
|
+
}
|
|
18618
|
+
function wrapToolsWithPersonaValidation(tools, ctx) {
|
|
18619
|
+
return tools.map((t) => {
|
|
18620
|
+
const docType = extractDocType(t.name);
|
|
18621
|
+
if (!docType) return t;
|
|
18622
|
+
return {
|
|
18623
|
+
...t,
|
|
18624
|
+
handler: async (args, extra) => {
|
|
18625
|
+
const persona = ctx.getActivePersona();
|
|
18626
|
+
if (!persona) {
|
|
18627
|
+
const summaries = buildPersonaSummaries();
|
|
18628
|
+
return {
|
|
18629
|
+
content: [
|
|
18630
|
+
{
|
|
18631
|
+
type: "text",
|
|
18632
|
+
text: `[PERSONA REQUIRED] You must set an active persona before creating or updating documents. Call the set_persona tool first.
|
|
18633
|
+
|
|
18634
|
+
${summaries}`
|
|
18635
|
+
}
|
|
18636
|
+
],
|
|
18637
|
+
isError: true
|
|
18638
|
+
};
|
|
18639
|
+
}
|
|
18640
|
+
const result = await t.handler(args, extra);
|
|
18641
|
+
if (ctx.isDocumentTypeAllowed(docType)) {
|
|
18642
|
+
return result;
|
|
18643
|
+
}
|
|
18644
|
+
const warning = [
|
|
18645
|
+
`[PERSONA WARNING] You are acting as ${persona.name} (${persona.shortName}). Creating/updating "${docType}" documents is outside your typical scope.`,
|
|
18646
|
+
"",
|
|
18647
|
+
`Your allowed document types: ${persona.documentTypes.join(", ")}`,
|
|
18648
|
+
"",
|
|
18649
|
+
"Consider whether this is the right persona for this task, or switch with set_persona.",
|
|
18650
|
+
"",
|
|
18651
|
+
"---"
|
|
18652
|
+
].join("\n");
|
|
18653
|
+
const content = Array.isArray(result.content) ? result.content.map(
|
|
18654
|
+
(block, index) => {
|
|
18655
|
+
if (index === 0 && block.type === "text" && block.text) {
|
|
18656
|
+
return { ...block, text: `${warning}
|
|
18657
|
+
${block.text}` };
|
|
18658
|
+
}
|
|
18659
|
+
return block;
|
|
18660
|
+
}
|
|
18661
|
+
) : result.content;
|
|
18662
|
+
return { ...result, content };
|
|
18663
|
+
}
|
|
18664
|
+
};
|
|
18665
|
+
});
|
|
18666
|
+
}
|
|
18667
|
+
|
|
18461
18668
|
// src/mcp/stdio-server.ts
|
|
18462
18669
|
function collectTools(marvinDir) {
|
|
18463
18670
|
const config2 = loadProjectConfig(marvinDir);
|
|
@@ -18520,8 +18727,11 @@ async function startStdioServer(options) {
|
|
|
18520
18727
|
{ name: "marvin-governance", version: "0.1.0" },
|
|
18521
18728
|
{ capabilities: { tools: {} } }
|
|
18522
18729
|
);
|
|
18523
|
-
const
|
|
18524
|
-
|
|
18730
|
+
const contextManager = new PersonaContextManager();
|
|
18731
|
+
const governanceTools = collectTools(options.marvinDir);
|
|
18732
|
+
const personaTools = createPersonaTools(contextManager, options.marvinDir);
|
|
18733
|
+
const wrappedTools = wrapToolsWithPersonaValidation(governanceTools, contextManager);
|
|
18734
|
+
registerSdkTools(server, [...personaTools, ...wrappedTools]);
|
|
18525
18735
|
const transport = new StdioServerTransport();
|
|
18526
18736
|
await server.connect(transport);
|
|
18527
18737
|
}
|
|
@@ -19476,7 +19686,7 @@ function createProgram() {
|
|
|
19476
19686
|
const program2 = new Command();
|
|
19477
19687
|
program2.name("marvin").description(
|
|
19478
19688
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
19479
|
-
).version("0.2.
|
|
19689
|
+
).version("0.2.1");
|
|
19480
19690
|
program2.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
19481
19691
|
await initCommand();
|
|
19482
19692
|
});
|