mrvn-cli 0.2.3 → 0.2.4

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 CHANGED
@@ -218,9 +218,9 @@ var DocumentStore = class {
218
218
  }
219
219
  }
220
220
  }
221
- list(query6) {
221
+ list(query7) {
222
222
  const results = [];
223
- const types = query6?.type ? [query6.type] : Object.keys(this.typeDirs);
223
+ const types = query7?.type ? [query7.type] : Object.keys(this.typeDirs);
224
224
  for (const type of types) {
225
225
  const dirName = this.typeDirs[type];
226
226
  if (!dirName) continue;
@@ -231,9 +231,9 @@ var DocumentStore = class {
231
231
  const filePath = path3.join(dir, file2);
232
232
  const raw = fs3.readFileSync(filePath, "utf-8");
233
233
  const doc = parseDocument(raw, filePath);
234
- if (query6?.status && doc.frontmatter.status !== query6.status) continue;
235
- if (query6?.owner && doc.frontmatter.owner !== query6.owner) continue;
236
- if (query6?.tag && (!doc.frontmatter.tags || !doc.frontmatter.tags.includes(query6.tag)))
234
+ if (query7?.status && doc.frontmatter.status !== query7.status) continue;
235
+ if (query7?.owner && doc.frontmatter.owner !== query7.owner) continue;
236
+ if (query7?.tag && (!doc.frontmatter.tags || !doc.frontmatter.tags.includes(query7.tag)))
237
237
  continue;
238
238
  results.push(doc);
239
239
  }
@@ -401,7 +401,8 @@ var productOwner = {
401
401
  "Acceptance criteria",
402
402
  "Feature definition and prioritization"
403
403
  ],
404
- documentTypes: ["decision", "question", "action", "feature"]
404
+ documentTypes: ["decision", "question", "action", "feature"],
405
+ contributionTypes: ["stakeholder-feedback", "acceptance-result", "priority-change", "market-insight"]
405
406
  };
406
407
 
407
408
  // src/personas/builtin/delivery-manager.ts
@@ -439,7 +440,8 @@ var deliveryManager = {
439
440
  "Status tracking",
440
441
  "Epic scheduling and tracking"
441
442
  ],
442
- documentTypes: ["action", "decision", "meeting", "question", "feature", "epic"]
443
+ documentTypes: ["action", "decision", "meeting", "question", "feature", "epic"],
444
+ contributionTypes: ["risk-finding", "blocker-report", "dependency-update", "status-assessment"]
443
445
  };
444
446
 
445
447
  // src/personas/builtin/tech-lead.ts
@@ -477,7 +479,8 @@ var techLead = {
477
479
  "Non-functional requirements",
478
480
  "Epic creation and scoping"
479
481
  ],
480
- documentTypes: ["decision", "action", "question", "epic"]
482
+ documentTypes: ["decision", "action", "question", "epic"],
483
+ contributionTypes: ["action-result", "spike-findings", "technical-assessment", "architecture-review"]
481
484
  };
482
485
 
483
486
  // src/personas/registry.ts
@@ -15851,19 +15854,141 @@ function createEpicTools(store) {
15851
15854
  ];
15852
15855
  }
15853
15856
 
15857
+ // src/plugins/builtin/tools/contributions.ts
15858
+ import { tool as tool11 } from "@anthropic-ai/claude-agent-sdk";
15859
+ function createContributionTools(store) {
15860
+ return [
15861
+ tool11(
15862
+ "list_contributions",
15863
+ "List all contributions in the project, optionally filtered by persona, contribution type, or status",
15864
+ {
15865
+ persona: external_exports.string().optional().describe("Filter by persona (e.g. 'tech-lead', 'product-owner')"),
15866
+ contributionType: external_exports.string().optional().describe("Filter by contribution type (e.g. 'action-result', 'risk-finding')"),
15867
+ status: external_exports.string().optional().describe("Filter by status")
15868
+ },
15869
+ async (args) => {
15870
+ let docs = store.list({ type: "contribution", status: args.status });
15871
+ if (args.persona) {
15872
+ docs = docs.filter((d) => d.frontmatter.persona === args.persona);
15873
+ }
15874
+ if (args.contributionType) {
15875
+ docs = docs.filter((d) => d.frontmatter.contributionType === args.contributionType);
15876
+ }
15877
+ const summary = docs.map((d) => ({
15878
+ id: d.frontmatter.id,
15879
+ title: d.frontmatter.title,
15880
+ status: d.frontmatter.status,
15881
+ persona: d.frontmatter.persona,
15882
+ contributionType: d.frontmatter.contributionType,
15883
+ aboutArtifact: d.frontmatter.aboutArtifact,
15884
+ created: d.frontmatter.created,
15885
+ tags: d.frontmatter.tags
15886
+ }));
15887
+ return {
15888
+ content: [{ type: "text", text: JSON.stringify(summary, null, 2) }]
15889
+ };
15890
+ },
15891
+ { annotations: { readOnly: true } }
15892
+ ),
15893
+ tool11(
15894
+ "get_contribution",
15895
+ "Get the full content of a specific contribution by ID",
15896
+ { id: external_exports.string().describe("Contribution ID (e.g. 'C-001')") },
15897
+ async (args) => {
15898
+ const doc = store.get(args.id);
15899
+ if (!doc) {
15900
+ return {
15901
+ content: [{ type: "text", text: `Contribution ${args.id} not found` }],
15902
+ isError: true
15903
+ };
15904
+ }
15905
+ return {
15906
+ content: [
15907
+ {
15908
+ type: "text",
15909
+ text: JSON.stringify(
15910
+ { ...doc.frontmatter, content: doc.content },
15911
+ null,
15912
+ 2
15913
+ )
15914
+ }
15915
+ ]
15916
+ };
15917
+ },
15918
+ { annotations: { readOnly: true } }
15919
+ ),
15920
+ tool11(
15921
+ "create_contribution",
15922
+ "Create a new contribution record from a persona",
15923
+ {
15924
+ title: external_exports.string().describe("Title of the contribution"),
15925
+ content: external_exports.string().describe("Contribution content \u2014 the input from the persona"),
15926
+ persona: external_exports.string().describe("Persona making the contribution (e.g. 'tech-lead')"),
15927
+ contributionType: external_exports.string().describe("Type of contribution (e.g. 'action-result', 'risk-finding')"),
15928
+ aboutArtifact: external_exports.string().optional().describe("Artifact ID this contribution relates to (e.g. 'A-001')"),
15929
+ status: external_exports.string().optional().describe("Status (default: 'open')"),
15930
+ tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization")
15931
+ },
15932
+ async (args) => {
15933
+ const frontmatter = {
15934
+ title: args.title,
15935
+ status: args.status ?? "open",
15936
+ persona: args.persona,
15937
+ contributionType: args.contributionType
15938
+ };
15939
+ if (args.aboutArtifact) frontmatter.aboutArtifact = args.aboutArtifact;
15940
+ if (args.tags) frontmatter.tags = args.tags;
15941
+ const doc = store.create("contribution", frontmatter, args.content);
15942
+ return {
15943
+ content: [
15944
+ {
15945
+ type: "text",
15946
+ text: `Created contribution ${doc.frontmatter.id}: ${doc.frontmatter.title}`
15947
+ }
15948
+ ]
15949
+ };
15950
+ }
15951
+ ),
15952
+ tool11(
15953
+ "update_contribution",
15954
+ "Update an existing contribution (e.g. to append an Effects section)",
15955
+ {
15956
+ id: external_exports.string().describe("Contribution ID to update"),
15957
+ title: external_exports.string().optional().describe("New title"),
15958
+ status: external_exports.string().optional().describe("New status"),
15959
+ content: external_exports.string().optional().describe("New content")
15960
+ },
15961
+ async (args) => {
15962
+ const { id, content, ...updates } = args;
15963
+ const doc = store.update(id, updates, content);
15964
+ return {
15965
+ content: [
15966
+ {
15967
+ type: "text",
15968
+ text: `Updated contribution ${doc.frontmatter.id}: ${doc.frontmatter.title}`
15969
+ }
15970
+ ]
15971
+ };
15972
+ }
15973
+ )
15974
+ ];
15975
+ }
15976
+
15854
15977
  // src/plugins/common.ts
15855
15978
  var COMMON_REGISTRATIONS = [
15856
15979
  { type: "meeting", dirName: "meetings", idPrefix: "M" },
15857
15980
  { type: "report", dirName: "reports", idPrefix: "R" },
15858
15981
  { type: "feature", dirName: "features", idPrefix: "F" },
15859
- { type: "epic", dirName: "epics", idPrefix: "E" }
15982
+ { type: "epic", dirName: "epics", idPrefix: "E" },
15983
+ { type: "contribution", dirName: "contributions", idPrefix: "C" }
15860
15984
  ];
15861
15985
  function createCommonTools(store) {
15862
15986
  return [
15863
15987
  ...createMeetingTools(store),
15864
15988
  ...createReportTools(store),
15865
15989
  ...createFeatureTools(store),
15866
- ...createEpicTools(store)
15990
+ ...createEpicTools(store),
15991
+ ...createContributionTools(store)
15867
15992
  ];
15868
15993
  }
15869
15994
 
@@ -15873,7 +15998,7 @@ var genericAgilePlugin = {
15873
15998
  name: "Generic Agile",
15874
15999
  description: "Default methodology plugin providing standard agile governance patterns for decisions, actions, and questions.",
15875
16000
  version: "0.1.0",
15876
- documentTypes: ["decision", "action", "question", "meeting", "report", "feature", "epic"],
16001
+ documentTypes: ["decision", "action", "question", "meeting", "report", "feature", "epic", "contribution"],
15877
16002
  documentTypeRegistrations: [...COMMON_REGISTRATIONS],
15878
16003
  tools: (store) => [...createCommonTools(store)],
15879
16004
  promptFragments: {
@@ -15894,7 +16019,13 @@ var genericAgilePlugin = {
15894
16019
  - Create features as "draft" and approve them when requirements are clear and prioritized.
15895
16020
  - Do NOT create epics \u2014 that is the Tech Lead's responsibility. You can view epics to track progress.
15896
16021
  - Use priority levels (critical, high, medium, low) to communicate business value.
15897
- - Tag features for categorization and cross-referencing.`,
16022
+ - Tag features for categorization and cross-referencing.
16023
+
16024
+ **Contribution Tools:**
16025
+ - **list_contributions** / **get_contribution**: Browse and read contribution records.
16026
+ - **create_contribution**: Record a contribution with persona, type, and optional related artifact.
16027
+ - **update_contribution**: Update a contribution (e.g. append effects).
16028
+ - Available contribution types: stakeholder-feedback, acceptance-result, priority-change, market-insight.`,
15898
16029
  "tech-lead": `You own epics and break approved features into implementation work.
15899
16030
 
15900
16031
  **Epic Tools:**
@@ -15915,7 +16046,13 @@ var genericAgilePlugin = {
15915
16046
  - Only create epics against approved features \u2014 create_epic enforces this.
15916
16047
  - Tag work items (actions, decisions, questions) with \`epic:E-xxx\` to group them under an epic.
15917
16048
  - Collaborate with the Delivery Manager on target dates and effort estimates.
15918
- - Each epic should have a clear scope and definition of done.`,
16049
+ - Each epic should have a clear scope and definition of done.
16050
+
16051
+ **Contribution Tools:**
16052
+ - **list_contributions** / **get_contribution**: Browse and read contribution records.
16053
+ - **create_contribution**: Record a contribution with persona, type, and optional related artifact.
16054
+ - **update_contribution**: Update a contribution (e.g. append effects).
16055
+ - Available contribution types: action-result, spike-findings, technical-assessment, architecture-review.`,
15919
16056
  "delivery-manager": `You track delivery across features and epics, manage schedules, and report on progress.
15920
16057
 
15921
16058
  **Report Tools:**
@@ -15943,7 +16080,13 @@ var genericAgilePlugin = {
15943
16080
  - After generating any report, offer to save it with save_report for audit trail.
15944
16081
  - Proactively flag risks: unowned actions, overdue items, epics linked to deferred features.
15945
16082
  - Use feature progress reports for stakeholder updates and epic progress for sprint-level tracking.
15946
- - Use analyze_meeting after meetings to extract outcomes into governance artifacts.`,
16083
+ - Use analyze_meeting after meetings to extract outcomes into governance artifacts.
16084
+
16085
+ **Contribution Tools:**
16086
+ - **list_contributions** / **get_contribution**: Browse and read contribution records.
16087
+ - **create_contribution**: Record a contribution with persona, type, and optional related artifact.
16088
+ - **update_contribution**: Update a contribution (e.g. append effects).
16089
+ - Available contribution types: risk-finding, blocker-report, dependency-update, status-assessment.`,
15947
16090
  "*": `You have access to feature, epic, and meeting tools for project coordination:
15948
16091
 
15949
16092
  **Features** (F-xxx): Product capabilities defined by the Product Owner. Features progress through draft \u2192 approved \u2192 done.
@@ -15955,15 +16098,20 @@ var genericAgilePlugin = {
15955
16098
  - **list_meetings** / **get_meeting**: Browse and read meeting records.
15956
16099
  - **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.
15957
16100
  - **update_meeting**: Update meeting status or notes.
15958
- - **analyze_meeting**: Analyze a meeting to extract decisions, actions, and questions as governance artifacts.`
16101
+ - **analyze_meeting**: Analyze a meeting to extract decisions, actions, and questions as governance artifacts.
16102
+
16103
+ **Contributions** (C-xxx): Structured inputs from personas outside of meetings (e.g. action results, risk findings, stakeholder feedback). Contributions are analyzed to produce governance effects.
16104
+ - **list_contributions** / **get_contribution**: Browse and read contribution records.
16105
+ - **create_contribution**: Record a contribution with persona, type, and optional related artifact.
16106
+ - **update_contribution**: Update a contribution (e.g. append effects).`
15959
16107
  }
15960
16108
  };
15961
16109
 
15962
16110
  // src/plugins/builtin/tools/use-cases.ts
15963
- import { tool as tool11 } from "@anthropic-ai/claude-agent-sdk";
16111
+ import { tool as tool12 } from "@anthropic-ai/claude-agent-sdk";
15964
16112
  function createUseCaseTools(store) {
15965
16113
  return [
15966
- tool11(
16114
+ tool12(
15967
16115
  "list_use_cases",
15968
16116
  "List all extension use cases, optionally filtered by status or extension type",
15969
16117
  {
@@ -15993,7 +16141,7 @@ function createUseCaseTools(store) {
15993
16141
  },
15994
16142
  { annotations: { readOnly: true } }
15995
16143
  ),
15996
- tool11(
16144
+ tool12(
15997
16145
  "get_use_case",
15998
16146
  "Get the full content of a specific use case by ID",
15999
16147
  { id: external_exports.string().describe("Use case ID (e.g. 'UC-001')") },
@@ -16020,7 +16168,7 @@ function createUseCaseTools(store) {
16020
16168
  },
16021
16169
  { annotations: { readOnly: true } }
16022
16170
  ),
16023
- tool11(
16171
+ tool12(
16024
16172
  "create_use_case",
16025
16173
  "Create a new extension use case definition (Phase 1: Assess Extension Use Case)",
16026
16174
  {
@@ -16054,7 +16202,7 @@ function createUseCaseTools(store) {
16054
16202
  };
16055
16203
  }
16056
16204
  ),
16057
- tool11(
16205
+ tool12(
16058
16206
  "update_use_case",
16059
16207
  "Update an existing extension use case",
16060
16208
  {
@@ -16084,10 +16232,10 @@ function createUseCaseTools(store) {
16084
16232
  }
16085
16233
 
16086
16234
  // src/plugins/builtin/tools/tech-assessments.ts
16087
- import { tool as tool12 } from "@anthropic-ai/claude-agent-sdk";
16235
+ import { tool as tool13 } from "@anthropic-ai/claude-agent-sdk";
16088
16236
  function createTechAssessmentTools(store) {
16089
16237
  return [
16090
- tool12(
16238
+ tool13(
16091
16239
  "list_tech_assessments",
16092
16240
  "List all technology assessments, optionally filtered by status",
16093
16241
  {
@@ -16118,7 +16266,7 @@ function createTechAssessmentTools(store) {
16118
16266
  },
16119
16267
  { annotations: { readOnly: true } }
16120
16268
  ),
16121
- tool12(
16269
+ tool13(
16122
16270
  "get_tech_assessment",
16123
16271
  "Get the full content of a specific technology assessment by ID",
16124
16272
  { id: external_exports.string().describe("Tech assessment ID (e.g. 'TA-001')") },
@@ -16145,7 +16293,7 @@ function createTechAssessmentTools(store) {
16145
16293
  },
16146
16294
  { annotations: { readOnly: true } }
16147
16295
  ),
16148
- tool12(
16296
+ tool13(
16149
16297
  "create_tech_assessment",
16150
16298
  "Create a new technology assessment linked to an assessed/approved use case (Phase 2: Assess Extension Technology)",
16151
16299
  {
@@ -16216,7 +16364,7 @@ function createTechAssessmentTools(store) {
16216
16364
  };
16217
16365
  }
16218
16366
  ),
16219
- tool12(
16367
+ tool13(
16220
16368
  "update_tech_assessment",
16221
16369
  "Update an existing technology assessment. The linked use case cannot be changed.",
16222
16370
  {
@@ -16246,10 +16394,10 @@ function createTechAssessmentTools(store) {
16246
16394
  }
16247
16395
 
16248
16396
  // src/plugins/builtin/tools/extension-designs.ts
16249
- import { tool as tool13 } from "@anthropic-ai/claude-agent-sdk";
16397
+ import { tool as tool14 } from "@anthropic-ai/claude-agent-sdk";
16250
16398
  function createExtensionDesignTools(store) {
16251
16399
  return [
16252
- tool13(
16400
+ tool14(
16253
16401
  "list_extension_designs",
16254
16402
  "List all extension designs, optionally filtered by status",
16255
16403
  {
@@ -16279,7 +16427,7 @@ function createExtensionDesignTools(store) {
16279
16427
  },
16280
16428
  { annotations: { readOnly: true } }
16281
16429
  ),
16282
- tool13(
16430
+ tool14(
16283
16431
  "get_extension_design",
16284
16432
  "Get the full content of a specific extension design by ID",
16285
16433
  { id: external_exports.string().describe("Extension design ID (e.g. 'XD-001')") },
@@ -16306,7 +16454,7 @@ function createExtensionDesignTools(store) {
16306
16454
  },
16307
16455
  { annotations: { readOnly: true } }
16308
16456
  ),
16309
- tool13(
16457
+ tool14(
16310
16458
  "create_extension_design",
16311
16459
  "Create a new extension design linked to a recommended tech assessment (Phase 3: Define Extension Target Solution)",
16312
16460
  {
@@ -16374,7 +16522,7 @@ function createExtensionDesignTools(store) {
16374
16522
  };
16375
16523
  }
16376
16524
  ),
16377
- tool13(
16525
+ tool14(
16378
16526
  "update_extension_design",
16379
16527
  "Update an existing extension design. The linked tech assessment cannot be changed.",
16380
16528
  {
@@ -16403,10 +16551,10 @@ function createExtensionDesignTools(store) {
16403
16551
  }
16404
16552
 
16405
16553
  // src/plugins/builtin/tools/aem-reports.ts
16406
- import { tool as tool14 } from "@anthropic-ai/claude-agent-sdk";
16554
+ import { tool as tool15 } from "@anthropic-ai/claude-agent-sdk";
16407
16555
  function createAemReportTools(store) {
16408
16556
  return [
16409
- tool14(
16557
+ tool15(
16410
16558
  "generate_extension_portfolio",
16411
16559
  "Generate a portfolio view of all use cases with their linked tech assessments and extension designs",
16412
16560
  {},
@@ -16458,7 +16606,7 @@ function createAemReportTools(store) {
16458
16606
  },
16459
16607
  { annotations: { readOnly: true } }
16460
16608
  ),
16461
- tool14(
16609
+ tool15(
16462
16610
  "generate_tech_readiness",
16463
16611
  "Generate a BTP technology readiness report showing service coverage and gaps across assessments",
16464
16612
  {},
@@ -16510,7 +16658,7 @@ function createAemReportTools(store) {
16510
16658
  },
16511
16659
  { annotations: { readOnly: true } }
16512
16660
  ),
16513
- tool14(
16661
+ tool15(
16514
16662
  "generate_phase_status",
16515
16663
  "Generate a phase progress report showing artifact counts and readiness per AEM phase",
16516
16664
  {},
@@ -16572,11 +16720,11 @@ function createAemReportTools(store) {
16572
16720
  import * as fs6 from "fs";
16573
16721
  import * as path6 from "path";
16574
16722
  import * as YAML4 from "yaml";
16575
- import { tool as tool15 } from "@anthropic-ai/claude-agent-sdk";
16723
+ import { tool as tool16 } from "@anthropic-ai/claude-agent-sdk";
16576
16724
  var PHASES = ["assess-use-case", "assess-technology", "define-solution"];
16577
16725
  function createAemPhaseTools(store, marvinDir) {
16578
16726
  return [
16579
- tool15(
16727
+ tool16(
16580
16728
  "get_current_phase",
16581
16729
  "Get the current AEM phase from project configuration",
16582
16730
  {},
@@ -16597,7 +16745,7 @@ function createAemPhaseTools(store, marvinDir) {
16597
16745
  },
16598
16746
  { annotations: { readOnly: true } }
16599
16747
  ),
16600
- tool15(
16748
+ tool16(
16601
16749
  "advance_phase",
16602
16750
  "Advance to the next AEM phase. Performs soft gate checks and warns if artifacts are incomplete, but does not block.",
16603
16751
  {
@@ -17372,7 +17520,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
17372
17520
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
17373
17521
 
17374
17522
  // src/skills/action-tools.ts
17375
- import { tool as tool16 } from "@anthropic-ai/claude-agent-sdk";
17523
+ import { tool as tool17 } from "@anthropic-ai/claude-agent-sdk";
17376
17524
 
17377
17525
  // src/skills/action-runner.ts
17378
17526
  import { query as query3 } from "@anthropic-ai/claude-agent-sdk";
@@ -17438,7 +17586,7 @@ function createSkillActionTools(skills, context) {
17438
17586
  if (!skill.actions) continue;
17439
17587
  for (const action of skill.actions) {
17440
17588
  tools.push(
17441
- tool16(
17589
+ tool17(
17442
17590
  `${skill.id}__${action.id}`,
17443
17591
  action.description,
17444
17592
  {
@@ -17530,10 +17678,10 @@ ${lines.join("\n\n")}`;
17530
17678
  }
17531
17679
 
17532
17680
  // src/mcp/persona-tools.ts
17533
- import { tool as tool17 } from "@anthropic-ai/claude-agent-sdk";
17681
+ import { tool as tool18 } from "@anthropic-ai/claude-agent-sdk";
17534
17682
  function createPersonaTools(ctx, marvinDir) {
17535
17683
  return [
17536
- tool17(
17684
+ tool18(
17537
17685
  "set_persona",
17538
17686
  "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.",
17539
17687
  {
@@ -17563,7 +17711,7 @@ ${summaries}`
17563
17711
  };
17564
17712
  }
17565
17713
  ),
17566
- tool17(
17714
+ tool18(
17567
17715
  "get_persona_guidance",
17568
17716
  "Get guidance for a persona without changing the active persona. If no persona is specified, lists all available personas with summaries.",
17569
17717
  {
@@ -19687,12 +19835,417 @@ Analyzing meeting: ${meetingDoc.frontmatter.title}`));
19687
19835
  });
19688
19836
  }
19689
19837
 
19838
+ // src/cli/commands/contribute.ts
19839
+ import chalk15 from "chalk";
19840
+
19841
+ // src/contributions/contribute.ts
19842
+ import chalk14 from "chalk";
19843
+ import ora5 from "ora";
19844
+ import { query as query6 } from "@anthropic-ai/claude-agent-sdk";
19845
+
19846
+ // src/contributions/prompts.ts
19847
+ function buildContributeSystemPrompt(persona, contributionType, projectConfig, isDraft) {
19848
+ const parts = [];
19849
+ parts.push(persona.systemPrompt);
19850
+ parts.push(`
19851
+ ## Project Context
19852
+ - **Project Name:** ${projectConfig.name}
19853
+ - **Methodology:** ${projectConfig.methodology ?? "Generic Agile"}
19854
+ `);
19855
+ parts.push(`
19856
+ ## Contribution Analysis Task
19857
+ You are processing a **${contributionType}** contribution from the **${persona.name}**.
19858
+ Review the contribution content and determine what governance effects should follow.
19859
+
19860
+ Possible effects include:
19861
+ 1. **Create** new artifacts \u2014 decisions (D-xxx), actions (A-xxx), questions (Q-xxx)
19862
+ 2. **Update** existing artifacts \u2014 change status, add information, close items
19863
+ `);
19864
+ parts.push(buildContributionTypeInstructions(contributionType));
19865
+ if (isDraft) {
19866
+ parts.push(`
19867
+ ## Mode: Draft Proposal
19868
+ Present your findings as a structured proposal. Do NOT create or update any artifacts.
19869
+ Format your response as:
19870
+
19871
+ ### Proposed Effects
19872
+ For each effect:
19873
+ - **Action:** [create/update]
19874
+ - **Target:** [new artifact type or existing artifact ID]
19875
+ - **Details:** [what would be created or changed]
19876
+ - **Rationale:** [why this effect follows from the contribution]
19877
+
19878
+ ### Summary
19879
+ Provide a brief summary of the contribution analysis and recommendations.
19880
+ `);
19881
+ } else {
19882
+ parts.push(`
19883
+ ## Mode: Direct Execution
19884
+ Use the MCP tools to create and update artifacts directly:
19885
+ - Use \`create_decision\` / \`create_action\` / \`create_question\` for new artifacts
19886
+ - Use \`update_decision\` / \`update_action\` / \`update_question\` to modify existing ones
19887
+ - Use \`list_decisions\` / \`list_actions\` / \`list_questions\` to check existing artifacts before creating or updating
19888
+
19889
+ Before creating artifacts, check existing ones using the list tools to avoid duplicates.
19890
+
19891
+ For EVERY artifact you create or update, include:
19892
+ - A \`source:<contribution-id>\` tag in the tags array (the contribution ID is provided in the user prompt)
19893
+ - Clear title and detailed content with context from the contribution
19894
+
19895
+ After processing all effects, provide a summary of what was created and updated.
19896
+ `);
19897
+ }
19898
+ return parts.join("\n");
19899
+ }
19900
+ function buildContributionTypeInstructions(contributionType) {
19901
+ const instructions = {
19902
+ "action-result": `
19903
+ ### Type-Specific Guidance: Action Result
19904
+ The contributor is reporting results of a completed action.
19905
+ - Update the referenced action's status to "done" if results are conclusive
19906
+ - Check if the results answer any open questions \u2014 if so, update those questions
19907
+ - Check if results challenge any existing decisions \u2014 if so, create a new question or update the decision
19908
+ - If results reveal new work, create new actions`,
19909
+ "spike-findings": `
19910
+ ### Type-Specific Guidance: Spike Findings
19911
+ The contributor is sharing findings from a technical investigation (spike).
19912
+ - Create decisions for any architectural or technical choices made
19913
+ - Update or close the original spike action if referenced
19914
+ - Create new actions for follow-up implementation work
19915
+ - Create questions for areas needing further investigation`,
19916
+ "technical-assessment": `
19917
+ ### Type-Specific Guidance: Technical Assessment
19918
+ The contributor is providing a technical evaluation.
19919
+ - Create decisions for technical recommendations
19920
+ - Create actions for remediation or improvement tasks
19921
+ - Create questions for areas needing more investigation
19922
+ - Flag any risks by tagging artifacts with "risk"`,
19923
+ "architecture-review": `
19924
+ ### Type-Specific Guidance: Architecture Review
19925
+ The contributor is documenting an architecture review.
19926
+ - Create decisions for architectural choices and trade-offs
19927
+ - Create actions for any architectural changes needed
19928
+ - Create questions for design concerns or alternatives to evaluate
19929
+ - Update existing decisions if the review validates or challenges them`,
19930
+ "stakeholder-feedback": `
19931
+ ### Type-Specific Guidance: Stakeholder Feedback
19932
+ The contributor is relaying feedback from stakeholders.
19933
+ - Create or update decisions based on stakeholder direction
19934
+ - Create actions for requested changes or follow-ups
19935
+ - Create questions for items needing clarification
19936
+ - Update priorities on existing artifacts if feedback changes priorities`,
19937
+ "acceptance-result": `
19938
+ ### Type-Specific Guidance: Acceptance Result
19939
+ The contributor is reporting results of acceptance testing.
19940
+ - Update the referenced action/feature status based on pass/fail
19941
+ - Create new actions for any rework needed
19942
+ - Create questions for ambiguous acceptance criteria
19943
+ - If accepted, mark related actions as "done"`,
19944
+ "priority-change": `
19945
+ ### Type-Specific Guidance: Priority Change
19946
+ The contributor is communicating a change in priorities.
19947
+ - Update priority fields on affected artifacts
19948
+ - Create decisions documenting the priority change rationale
19949
+ - Create actions for any re-planning needed
19950
+ - Flag any blocked or impacted items`,
19951
+ "market-insight": `
19952
+ ### Type-Specific Guidance: Market Insight
19953
+ The contributor is sharing market intelligence or competitive information.
19954
+ - Create decisions if the insight drives product direction changes
19955
+ - Create questions for areas needing further market research
19956
+ - Create actions for competitive response tasks
19957
+ - Update existing features or priorities if affected`,
19958
+ "risk-finding": `
19959
+ ### Type-Specific Guidance: Risk Finding
19960
+ The contributor is identifying a project risk.
19961
+ - Create actions for risk mitigation tasks
19962
+ - Create decisions for risk response strategies
19963
+ - Create questions for risks needing further assessment
19964
+ - Tag all related artifacts with "risk" for tracking`,
19965
+ "blocker-report": `
19966
+ ### Type-Specific Guidance: Blocker Report
19967
+ The contributor is reporting a blocker.
19968
+ - Create actions to resolve the blocker
19969
+ - Update blocked artifacts' status or add blocking context
19970
+ - Create questions for blockers needing external input
19971
+ - Escalate by creating high-priority actions if critical`,
19972
+ "dependency-update": `
19973
+ ### Type-Specific Guidance: Dependency Update
19974
+ The contributor is reporting on external or internal dependencies.
19975
+ - Update affected actions with dependency information
19976
+ - Create new actions for dependency-related tasks
19977
+ - Create questions for uncertain dependencies
19978
+ - Update timelines or flag schedule risks`,
19979
+ "status-assessment": `
19980
+ ### Type-Specific Guidance: Status Assessment
19981
+ The contributor is providing a delivery status assessment.
19982
+ - Update action statuses based on assessment
19983
+ - Create actions for corrective measures
19984
+ - Create questions for items needing investigation
19985
+ - Create decisions for any process changes needed`
19986
+ };
19987
+ return instructions[contributionType] ?? `
19988
+ ### Type-Specific Guidance
19989
+ Analyze the contribution and determine appropriate governance effects based on the content.`;
19990
+ }
19991
+ function buildContributeUserPrompt(contributionId, contributionType, content, aboutArtifact, isDraft) {
19992
+ const mode = isDraft ? "propose" : "execute";
19993
+ const aboutLine = aboutArtifact ? `
19994
+ **Related Artifact:** ${aboutArtifact}` : "";
19995
+ return `Please analyze the following contribution and ${mode} governance effects.
19996
+
19997
+ **Contribution ID:** ${contributionId}
19998
+ **Contribution Type:** ${contributionType}${aboutLine}
19999
+
20000
+ ---
20001
+ ${content}
20002
+ ---
20003
+
20004
+ Analyze this contribution thoroughly and ${mode} all relevant governance effects (create new artifacts, update existing ones). Tag each created or updated artifact with "source:${contributionId}" for traceability.`;
20005
+ }
20006
+
20007
+ // src/contributions/contribute.ts
20008
+ async function contributeFromPersona(options) {
20009
+ const { marvinDir, prompt, aboutArtifact, draft } = options;
20010
+ const config2 = getConfig(marvinDir);
20011
+ const personaId = resolvePersonaId(options.persona);
20012
+ const persona = getPersona(personaId);
20013
+ const allowedTypes = persona.contributionTypes ?? [];
20014
+ if (allowedTypes.length > 0 && !allowedTypes.includes(options.contributionType)) {
20015
+ throw new Error(
20016
+ `Contribution type "${options.contributionType}" is not valid for persona "${persona.name}". Allowed types: ${allowedTypes.join(", ")}`
20017
+ );
20018
+ }
20019
+ const plugin = resolvePlugin(config2.project.methodology);
20020
+ const registrations = plugin?.documentTypeRegistrations ?? [];
20021
+ const store = new DocumentStore(marvinDir, registrations);
20022
+ const contributionFrontmatter = {
20023
+ title: `${options.contributionType}: ${prompt.slice(0, 60)}${prompt.length > 60 ? "..." : ""}`,
20024
+ status: "open",
20025
+ persona: personaId,
20026
+ contributionType: options.contributionType
20027
+ };
20028
+ if (aboutArtifact) contributionFrontmatter.aboutArtifact = aboutArtifact;
20029
+ const contributionDoc = store.create("contribution", contributionFrontmatter, prompt);
20030
+ const contributionId = contributionDoc.frontmatter.id;
20031
+ const createdArtifacts = [];
20032
+ const updatedArtifacts = [];
20033
+ if (!draft) {
20034
+ const originalCreate = store.create.bind(store);
20035
+ store.create = (type, frontmatter, content) => {
20036
+ const tags = frontmatter.tags ?? [];
20037
+ const sourceTag = `source:${contributionId}`;
20038
+ if (!tags.includes(sourceTag)) {
20039
+ tags.push(sourceTag);
20040
+ }
20041
+ const doc = originalCreate(type, { ...frontmatter, source: contributionId, tags }, content);
20042
+ createdArtifacts.push(doc.frontmatter.id);
20043
+ return doc;
20044
+ };
20045
+ const originalUpdate = store.update.bind(store);
20046
+ store.update = (id, updates, content) => {
20047
+ if (id === contributionId) {
20048
+ return originalUpdate(id, updates, content);
20049
+ }
20050
+ const existing = store.get(id);
20051
+ const existingTags = existing?.frontmatter.tags ?? [];
20052
+ const sourceTag = `source:${contributionId}`;
20053
+ if (!existingTags.includes(sourceTag)) {
20054
+ existingTags.push(sourceTag);
20055
+ }
20056
+ const doc = originalUpdate(id, { ...updates, tags: existingTags }, content);
20057
+ if (!updatedArtifacts.includes(id)) {
20058
+ updatedArtifacts.push(id);
20059
+ }
20060
+ return doc;
20061
+ };
20062
+ }
20063
+ const pluginTools = plugin ? getPluginTools(plugin, store, marvinDir) : [];
20064
+ const mcpServer = createMarvinMcpServer(store, { pluginTools });
20065
+ const systemPrompt = buildContributeSystemPrompt(persona, options.contributionType, config2.project, draft);
20066
+ const userPrompt = buildContributeUserPrompt(
20067
+ contributionId,
20068
+ options.contributionType,
20069
+ prompt,
20070
+ aboutArtifact,
20071
+ draft
20072
+ );
20073
+ const spinner = ora5({ text: `Processing contribution ${contributionId}...`, color: "cyan" });
20074
+ spinner.start();
20075
+ try {
20076
+ const allowedTools = draft ? [
20077
+ "mcp__marvin-governance__list_decisions",
20078
+ "mcp__marvin-governance__list_actions",
20079
+ "mcp__marvin-governance__list_questions",
20080
+ "mcp__marvin-governance__get_decision",
20081
+ "mcp__marvin-governance__get_action",
20082
+ "mcp__marvin-governance__get_question"
20083
+ ] : [
20084
+ "mcp__marvin-governance__create_decision",
20085
+ "mcp__marvin-governance__create_action",
20086
+ "mcp__marvin-governance__create_question",
20087
+ "mcp__marvin-governance__update_decision",
20088
+ "mcp__marvin-governance__update_action",
20089
+ "mcp__marvin-governance__update_question",
20090
+ "mcp__marvin-governance__list_decisions",
20091
+ "mcp__marvin-governance__list_actions",
20092
+ "mcp__marvin-governance__list_questions",
20093
+ "mcp__marvin-governance__get_decision",
20094
+ "mcp__marvin-governance__get_action",
20095
+ "mcp__marvin-governance__get_question"
20096
+ ];
20097
+ const conversation = query6({
20098
+ prompt: userPrompt,
20099
+ options: {
20100
+ systemPrompt,
20101
+ mcpServers: { "marvin-governance": mcpServer },
20102
+ permissionMode: "acceptEdits",
20103
+ maxTurns: 10,
20104
+ tools: [],
20105
+ allowedTools
20106
+ }
20107
+ });
20108
+ for await (const message of conversation) {
20109
+ handleContributeMessage(message, spinner);
20110
+ }
20111
+ const effects = [...createdArtifacts, ...updatedArtifacts];
20112
+ if (!draft && effects.length > 0) {
20113
+ appendEffectsToContribution(store, contributionId, contributionDoc.content, createdArtifacts, updatedArtifacts, store);
20114
+ }
20115
+ spinner.stop();
20116
+ if (draft) {
20117
+ console.log(chalk14.dim(`
20118
+ Draft proposal complete. No artifacts were created or updated.`));
20119
+ console.log(chalk14.dim(`Use --no-draft to execute effects directly.`));
20120
+ } else {
20121
+ const totalEffects = createdArtifacts.length + updatedArtifacts.length;
20122
+ console.log(chalk14.green(`
20123
+ Contribution ${contributionId} processed: ${totalEffects} effect${totalEffects === 1 ? "" : "s"}`));
20124
+ if (createdArtifacts.length > 0) {
20125
+ console.log(chalk14.dim(` Created: ${createdArtifacts.join(", ")}`));
20126
+ }
20127
+ if (updatedArtifacts.length > 0) {
20128
+ console.log(chalk14.dim(` Updated: ${updatedArtifacts.join(", ")}`));
20129
+ }
20130
+ }
20131
+ return { contributionId, effects, draft };
20132
+ } catch (err) {
20133
+ spinner.stop();
20134
+ throw err;
20135
+ }
20136
+ }
20137
+ function appendEffectsToContribution(store, contributionId, existingContent, created, updated, storeInstance) {
20138
+ const lines = [];
20139
+ if (created.length > 0) {
20140
+ lines.push("### Created");
20141
+ for (const id of created) {
20142
+ const doc = storeInstance.get(id);
20143
+ const title = doc ? doc.frontmatter.title : id;
20144
+ lines.push(`- ${id}: ${title}`);
20145
+ }
20146
+ }
20147
+ if (updated.length > 0) {
20148
+ lines.push("### Updated");
20149
+ for (const id of updated) {
20150
+ const doc = storeInstance.get(id);
20151
+ const title = doc ? doc.frontmatter.title : id;
20152
+ lines.push(`- ${id}: ${title}`);
20153
+ }
20154
+ }
20155
+ const effectsSection = `
20156
+
20157
+ ## Effects
20158
+ ${lines.join("\n")}`;
20159
+ const updatedContent = existingContent + effectsSection;
20160
+ store.update(contributionId, { status: "processed" }, updatedContent);
20161
+ }
20162
+ function handleContributeMessage(message, spinner) {
20163
+ switch (message.type) {
20164
+ case "assistant": {
20165
+ spinner.stop();
20166
+ const textBlocks = message.message.content.filter(
20167
+ (b) => b.type === "text"
20168
+ );
20169
+ if (textBlocks.length > 0) {
20170
+ console.log(
20171
+ chalk14.cyan("\nMarvin: ") + textBlocks.map((b) => b.text).join("\n")
20172
+ );
20173
+ }
20174
+ break;
20175
+ }
20176
+ case "system": {
20177
+ if (message.subtype === "init") {
20178
+ spinner.start("Analyzing contribution...");
20179
+ }
20180
+ break;
20181
+ }
20182
+ case "result": {
20183
+ spinner.stop();
20184
+ if (message.subtype !== "success") {
20185
+ console.log(
20186
+ chalk14.red(`
20187
+ Contribution analysis ended with error: ${message.subtype}`)
20188
+ );
20189
+ }
20190
+ break;
20191
+ }
20192
+ }
20193
+ }
20194
+
20195
+ // src/cli/commands/contribute.ts
20196
+ async function contributeCommand(options) {
20197
+ const project = loadProject();
20198
+ const marvinDir = project.marvinDir;
20199
+ if (!options.as) {
20200
+ console.log(chalk15.red("Missing required option: --as <persona>"));
20201
+ console.log(chalk15.dim('Example: marvin contribute --as tl --type action-result --prompt "..."'));
20202
+ return;
20203
+ }
20204
+ if (!options.type) {
20205
+ console.log(chalk15.red("Missing required option: --type <contribution-type>"));
20206
+ const personaId2 = resolvePersonaId(options.as);
20207
+ const persona2 = getPersona(personaId2);
20208
+ if (persona2?.contributionTypes?.length) {
20209
+ console.log(chalk15.dim(`Available types for ${persona2.name}: ${persona2.contributionTypes.join(", ")}`));
20210
+ }
20211
+ return;
20212
+ }
20213
+ if (!options.prompt) {
20214
+ console.log(chalk15.red("Missing required option: --prompt <text>"));
20215
+ console.log(chalk15.dim("Provide the contribution content via --prompt."));
20216
+ return;
20217
+ }
20218
+ const personaId = resolvePersonaId(options.as);
20219
+ const persona = getPersona(personaId);
20220
+ if (!persona) {
20221
+ console.log(chalk15.red(`Unknown persona: ${options.as}`));
20222
+ return;
20223
+ }
20224
+ const isDraft = options.draft !== false;
20225
+ console.log(chalk15.bold(`
20226
+ Contribution: ${options.type}`));
20227
+ console.log(chalk15.dim(`Persona: ${persona.name}`));
20228
+ console.log(chalk15.dim(`Mode: ${isDraft ? "draft (propose only)" : "direct (execute effects)"}`));
20229
+ if (options.about) {
20230
+ console.log(chalk15.dim(`About: ${options.about}`));
20231
+ }
20232
+ console.log();
20233
+ await contributeFromPersona({
20234
+ marvinDir,
20235
+ persona: options.as,
20236
+ contributionType: options.type,
20237
+ prompt: options.prompt,
20238
+ aboutArtifact: options.about,
20239
+ draft: isDraft
20240
+ });
20241
+ }
20242
+
19690
20243
  // src/cli/program.ts
19691
20244
  function createProgram() {
19692
20245
  const program = new Command();
19693
20246
  program.name("marvin").description(
19694
20247
  "AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
19695
- ).version("0.2.3");
20248
+ ).version("0.2.4");
19696
20249
  program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
19697
20250
  await initCommand();
19698
20251
  });
@@ -19735,6 +20288,9 @@ function createProgram() {
19735
20288
  program.command("analyze <meeting-id>").description("Analyze a meeting to extract decisions, actions, and questions").option("--draft", "Propose artifacts without creating them (default)").option("--no-draft", "Create artifacts directly via MCP tools").option("--as <persona>", "Persona for analysis (default: delivery-manager)").action(async (meetingId, options) => {
19736
20289
  await analyzeCommand(meetingId, options);
19737
20290
  });
20291
+ program.command("contribute").description("Submit a structured contribution from a persona to generate governance effects").requiredOption("--as <persona>", "Persona making the contribution (po, dm, tl)").requiredOption("--type <type>", "Contribution type (e.g. action-result, risk-finding)").requiredOption("--prompt <text>", "Contribution content").option("--about <artifact-id>", "Related artifact ID (e.g. A-001)").option("--draft", "Propose effects without executing (default)").option("--no-draft", "Execute effects directly").action(async (options) => {
20292
+ await contributeCommand(options);
20293
+ });
19738
20294
  program.command("import <path>").description("Import documents or sources from external paths").option("--dry-run", "Preview without writing files").option(
19739
20295
  "--conflict <strategy>",
19740
20296
  "ID conflict strategy: renumber, skip, overwrite (default: renumber)"