mrvn-cli 0.2.3 → 0.2.5

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/marvin.js CHANGED
@@ -14313,6 +14313,60 @@ function createReportTools(store) {
14313
14313
  },
14314
14314
  { annotations: { readOnly: true } }
14315
14315
  ),
14316
+ tool2(
14317
+ "generate_sprint_progress",
14318
+ "Generate progress report for a sprint or all sprints, showing linked epics and tagged work items",
14319
+ {
14320
+ sprint: external_exports.string().optional().describe("Specific sprint ID (e.g. 'SP-001') or omit for all")
14321
+ },
14322
+ async (args) => {
14323
+ const allDocs = store.list();
14324
+ const sprintDocs = store.list({ type: "sprint" });
14325
+ const sprints = sprintDocs.filter((s) => !args.sprint || s.frontmatter.id === args.sprint).map((sprintDoc) => {
14326
+ const sprintId = sprintDoc.frontmatter.id;
14327
+ const linkedEpicIds = sprintDoc.frontmatter.linkedEpics ?? [];
14328
+ const linkedEpics = linkedEpicIds.map((epicId) => {
14329
+ const epic = store.get(epicId);
14330
+ return epic ? { id: epicId, title: epic.frontmatter.title, status: epic.frontmatter.status } : { id: epicId, title: "(not found)", status: "unknown" };
14331
+ });
14332
+ const workItems = allDocs.filter(
14333
+ (d) => d.frontmatter.type !== "sprint" && d.frontmatter.type !== "epic" && d.frontmatter.tags?.includes(`sprint:${sprintId}`)
14334
+ );
14335
+ const byStatus = {};
14336
+ for (const doc of workItems) {
14337
+ byStatus[doc.frontmatter.status] = (byStatus[doc.frontmatter.status] ?? 0) + 1;
14338
+ }
14339
+ const doneCount = (byStatus["done"] ?? 0) + (byStatus["resolved"] ?? 0) + (byStatus["closed"] ?? 0);
14340
+ const total = workItems.length;
14341
+ const completionPct = total > 0 ? Math.round(doneCount / total * 100) : 0;
14342
+ return {
14343
+ id: sprintDoc.frontmatter.id,
14344
+ title: sprintDoc.frontmatter.title,
14345
+ status: sprintDoc.frontmatter.status,
14346
+ goal: sprintDoc.frontmatter.goal,
14347
+ startDate: sprintDoc.frontmatter.startDate,
14348
+ endDate: sprintDoc.frontmatter.endDate,
14349
+ linkedEpics,
14350
+ workItems: {
14351
+ total,
14352
+ done: doneCount,
14353
+ completionPct,
14354
+ byStatus,
14355
+ items: workItems.map((d) => ({
14356
+ id: d.frontmatter.id,
14357
+ title: d.frontmatter.title,
14358
+ type: d.frontmatter.type,
14359
+ status: d.frontmatter.status
14360
+ }))
14361
+ }
14362
+ };
14363
+ });
14364
+ return {
14365
+ content: [{ type: "text", text: JSON.stringify({ sprints }, null, 2) }]
14366
+ };
14367
+ },
14368
+ { annotations: { readOnly: true } }
14369
+ ),
14316
14370
  tool2(
14317
14371
  "generate_feature_progress",
14318
14372
  "Generate progress report for features and their linked epics",
@@ -14359,7 +14413,7 @@ function createReportTools(store) {
14359
14413
  {
14360
14414
  title: external_exports.string().describe("Report title"),
14361
14415
  content: external_exports.string().describe("Full report content in markdown"),
14362
- reportType: external_exports.enum(["status", "risk-register", "gar", "epic-progress", "feature-progress", "custom"]).describe("Type of report"),
14416
+ reportType: external_exports.enum(["status", "risk-register", "gar", "epic-progress", "feature-progress", "sprint-progress", "custom"]).describe("Type of report"),
14363
14417
  tags: external_exports.array(external_exports.string()).optional().describe("Additional tags")
14364
14418
  },
14365
14419
  async (args) => {
@@ -14656,19 +14710,316 @@ function createEpicTools(store) {
14656
14710
  ];
14657
14711
  }
14658
14712
 
14713
+ // src/plugins/builtin/tools/contributions.ts
14714
+ import { tool as tool5 } from "@anthropic-ai/claude-agent-sdk";
14715
+ function createContributionTools(store) {
14716
+ return [
14717
+ tool5(
14718
+ "list_contributions",
14719
+ "List all contributions in the project, optionally filtered by persona, contribution type, or status",
14720
+ {
14721
+ persona: external_exports.string().optional().describe("Filter by persona (e.g. 'tech-lead', 'product-owner')"),
14722
+ contributionType: external_exports.string().optional().describe("Filter by contribution type (e.g. 'action-result', 'risk-finding')"),
14723
+ status: external_exports.string().optional().describe("Filter by status")
14724
+ },
14725
+ async (args) => {
14726
+ let docs = store.list({ type: "contribution", status: args.status });
14727
+ if (args.persona) {
14728
+ docs = docs.filter((d) => d.frontmatter.persona === args.persona);
14729
+ }
14730
+ if (args.contributionType) {
14731
+ docs = docs.filter((d) => d.frontmatter.contributionType === args.contributionType);
14732
+ }
14733
+ const summary = docs.map((d) => ({
14734
+ id: d.frontmatter.id,
14735
+ title: d.frontmatter.title,
14736
+ status: d.frontmatter.status,
14737
+ persona: d.frontmatter.persona,
14738
+ contributionType: d.frontmatter.contributionType,
14739
+ aboutArtifact: d.frontmatter.aboutArtifact,
14740
+ created: d.frontmatter.created,
14741
+ tags: d.frontmatter.tags
14742
+ }));
14743
+ return {
14744
+ content: [{ type: "text", text: JSON.stringify(summary, null, 2) }]
14745
+ };
14746
+ },
14747
+ { annotations: { readOnly: true } }
14748
+ ),
14749
+ tool5(
14750
+ "get_contribution",
14751
+ "Get the full content of a specific contribution by ID",
14752
+ { id: external_exports.string().describe("Contribution ID (e.g. 'C-001')") },
14753
+ async (args) => {
14754
+ const doc = store.get(args.id);
14755
+ if (!doc) {
14756
+ return {
14757
+ content: [{ type: "text", text: `Contribution ${args.id} not found` }],
14758
+ isError: true
14759
+ };
14760
+ }
14761
+ return {
14762
+ content: [
14763
+ {
14764
+ type: "text",
14765
+ text: JSON.stringify(
14766
+ { ...doc.frontmatter, content: doc.content },
14767
+ null,
14768
+ 2
14769
+ )
14770
+ }
14771
+ ]
14772
+ };
14773
+ },
14774
+ { annotations: { readOnly: true } }
14775
+ ),
14776
+ tool5(
14777
+ "create_contribution",
14778
+ "Create a new contribution record from a persona",
14779
+ {
14780
+ title: external_exports.string().describe("Title of the contribution"),
14781
+ content: external_exports.string().describe("Contribution content \u2014 the input from the persona"),
14782
+ persona: external_exports.string().describe("Persona making the contribution (e.g. 'tech-lead')"),
14783
+ contributionType: external_exports.string().describe("Type of contribution (e.g. 'action-result', 'risk-finding')"),
14784
+ aboutArtifact: external_exports.string().optional().describe("Artifact ID this contribution relates to (e.g. 'A-001')"),
14785
+ status: external_exports.string().optional().describe("Status (default: 'open')"),
14786
+ tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization")
14787
+ },
14788
+ async (args) => {
14789
+ const frontmatter = {
14790
+ title: args.title,
14791
+ status: args.status ?? "open",
14792
+ persona: args.persona,
14793
+ contributionType: args.contributionType
14794
+ };
14795
+ if (args.aboutArtifact) frontmatter.aboutArtifact = args.aboutArtifact;
14796
+ if (args.tags) frontmatter.tags = args.tags;
14797
+ const doc = store.create("contribution", frontmatter, args.content);
14798
+ return {
14799
+ content: [
14800
+ {
14801
+ type: "text",
14802
+ text: `Created contribution ${doc.frontmatter.id}: ${doc.frontmatter.title}`
14803
+ }
14804
+ ]
14805
+ };
14806
+ }
14807
+ ),
14808
+ tool5(
14809
+ "update_contribution",
14810
+ "Update an existing contribution (e.g. to append an Effects section)",
14811
+ {
14812
+ id: external_exports.string().describe("Contribution ID to update"),
14813
+ title: external_exports.string().optional().describe("New title"),
14814
+ status: external_exports.string().optional().describe("New status"),
14815
+ content: external_exports.string().optional().describe("New content")
14816
+ },
14817
+ async (args) => {
14818
+ const { id, content, ...updates } = args;
14819
+ const doc = store.update(id, updates, content);
14820
+ return {
14821
+ content: [
14822
+ {
14823
+ type: "text",
14824
+ text: `Updated contribution ${doc.frontmatter.id}: ${doc.frontmatter.title}`
14825
+ }
14826
+ ]
14827
+ };
14828
+ }
14829
+ )
14830
+ ];
14831
+ }
14832
+
14833
+ // src/plugins/builtin/tools/sprints.ts
14834
+ import { tool as tool6 } from "@anthropic-ai/claude-agent-sdk";
14835
+ function createSprintTools(store) {
14836
+ return [
14837
+ tool6(
14838
+ "list_sprints",
14839
+ "List all sprints in the project, optionally filtered by status",
14840
+ {
14841
+ status: external_exports.enum(["planned", "active", "completed", "cancelled"]).optional().describe("Filter by sprint status")
14842
+ },
14843
+ async (args) => {
14844
+ const docs = store.list({ type: "sprint", status: args.status });
14845
+ const summary = docs.map((d) => ({
14846
+ id: d.frontmatter.id,
14847
+ title: d.frontmatter.title,
14848
+ status: d.frontmatter.status,
14849
+ goal: d.frontmatter.goal,
14850
+ startDate: d.frontmatter.startDate,
14851
+ endDate: d.frontmatter.endDate,
14852
+ linkedEpics: d.frontmatter.linkedEpics,
14853
+ tags: d.frontmatter.tags
14854
+ }));
14855
+ return {
14856
+ content: [{ type: "text", text: JSON.stringify(summary, null, 2) }]
14857
+ };
14858
+ },
14859
+ { annotations: { readOnly: true } }
14860
+ ),
14861
+ tool6(
14862
+ "get_sprint",
14863
+ "Get the full content of a specific sprint by ID",
14864
+ { id: external_exports.string().describe("Sprint ID (e.g. 'SP-001')") },
14865
+ async (args) => {
14866
+ const doc = store.get(args.id);
14867
+ if (!doc) {
14868
+ return {
14869
+ content: [{ type: "text", text: `Sprint ${args.id} not found` }],
14870
+ isError: true
14871
+ };
14872
+ }
14873
+ return {
14874
+ content: [
14875
+ {
14876
+ type: "text",
14877
+ text: JSON.stringify(
14878
+ { ...doc.frontmatter, content: doc.content },
14879
+ null,
14880
+ 2
14881
+ )
14882
+ }
14883
+ ]
14884
+ };
14885
+ },
14886
+ { annotations: { readOnly: true } }
14887
+ ),
14888
+ tool6(
14889
+ "create_sprint",
14890
+ "Create a new sprint with dates, goal, and optionally linked epics",
14891
+ {
14892
+ title: external_exports.string().describe("Sprint title"),
14893
+ content: external_exports.string().describe("Sprint description and objectives"),
14894
+ goal: external_exports.string().describe("Sprint goal \u2014 what this sprint aims to deliver"),
14895
+ startDate: external_exports.string().describe("Sprint start date (ISO format, e.g. '2026-03-01')"),
14896
+ endDate: external_exports.string().describe("Sprint end date (ISO format, e.g. '2026-03-14')"),
14897
+ status: external_exports.enum(["planned", "active", "completed", "cancelled"]).optional().describe("Sprint status (default: 'planned')"),
14898
+ linkedEpics: external_exports.array(external_exports.string()).optional().describe("Epic IDs to link (e.g. ['E-001', 'E-003']). Soft-validated: warns if not found but still creates."),
14899
+ tags: external_exports.array(external_exports.string()).optional().describe("Additional tags")
14900
+ },
14901
+ async (args) => {
14902
+ const warnings = [];
14903
+ if (args.linkedEpics) {
14904
+ for (const epicId of args.linkedEpics) {
14905
+ const epic = store.get(epicId);
14906
+ if (!epic) {
14907
+ warnings.push(`Epic ${epicId} not found (linked anyway)`);
14908
+ }
14909
+ }
14910
+ }
14911
+ const frontmatter = {
14912
+ title: args.title,
14913
+ status: args.status ?? "planned",
14914
+ goal: args.goal,
14915
+ startDate: args.startDate,
14916
+ endDate: args.endDate,
14917
+ linkedEpics: args.linkedEpics ?? [],
14918
+ tags: [...args.tags ?? []]
14919
+ };
14920
+ const doc = store.create("sprint", frontmatter, args.content);
14921
+ const sprintId = doc.frontmatter.id;
14922
+ if (args.linkedEpics) {
14923
+ for (const epicId of args.linkedEpics) {
14924
+ const epic = store.get(epicId);
14925
+ if (epic) {
14926
+ const existingTags = epic.frontmatter.tags ?? [];
14927
+ const sprintTag = `sprint:${sprintId}`;
14928
+ if (!existingTags.includes(sprintTag)) {
14929
+ store.update(epicId, { tags: [...existingTags, sprintTag] });
14930
+ }
14931
+ }
14932
+ }
14933
+ }
14934
+ const parts = [`Created sprint ${sprintId}: ${doc.frontmatter.title}`];
14935
+ if (warnings.length > 0) {
14936
+ parts.push(`Warnings: ${warnings.join("; ")}`);
14937
+ }
14938
+ return {
14939
+ content: [{ type: "text", text: parts.join("\n") }]
14940
+ };
14941
+ }
14942
+ ),
14943
+ tool6(
14944
+ "update_sprint",
14945
+ "Update an existing sprint. Cannot change id or type.",
14946
+ {
14947
+ id: external_exports.string().describe("Sprint ID to update"),
14948
+ title: external_exports.string().optional().describe("New title"),
14949
+ status: external_exports.enum(["planned", "active", "completed", "cancelled"]).optional().describe("New status"),
14950
+ content: external_exports.string().optional().describe("New content"),
14951
+ goal: external_exports.string().optional().describe("New sprint goal"),
14952
+ startDate: external_exports.string().optional().describe("New start date"),
14953
+ endDate: external_exports.string().optional().describe("New end date"),
14954
+ linkedEpics: external_exports.array(external_exports.string()).optional().describe("New list of linked epic IDs (replaces existing)"),
14955
+ tags: external_exports.array(external_exports.string()).optional().describe("New tags (replaces existing)")
14956
+ },
14957
+ async (args) => {
14958
+ const { id, content, linkedEpics, ...updates } = args;
14959
+ const existing = store.get(id);
14960
+ if (!existing) {
14961
+ return {
14962
+ content: [{ type: "text", text: `Sprint ${id} not found` }],
14963
+ isError: true
14964
+ };
14965
+ }
14966
+ if (linkedEpics !== void 0) {
14967
+ const oldLinked = existing.frontmatter.linkedEpics ?? [];
14968
+ const sprintTag = `sprint:${id}`;
14969
+ const removed = oldLinked.filter((e) => !linkedEpics.includes(e));
14970
+ for (const epicId of removed) {
14971
+ const epic = store.get(epicId);
14972
+ if (epic) {
14973
+ const tags = epic.frontmatter.tags ?? [];
14974
+ const filtered = tags.filter((t) => t !== sprintTag);
14975
+ if (filtered.length !== tags.length) {
14976
+ store.update(epicId, { tags: filtered });
14977
+ }
14978
+ }
14979
+ }
14980
+ const added = linkedEpics.filter((e) => !oldLinked.includes(e));
14981
+ for (const epicId of added) {
14982
+ const epic = store.get(epicId);
14983
+ if (epic) {
14984
+ const tags = epic.frontmatter.tags ?? [];
14985
+ if (!tags.includes(sprintTag)) {
14986
+ store.update(epicId, { tags: [...tags, sprintTag] });
14987
+ }
14988
+ }
14989
+ }
14990
+ updates.linkedEpics = linkedEpics;
14991
+ }
14992
+ const doc = store.update(id, updates, content);
14993
+ return {
14994
+ content: [
14995
+ {
14996
+ type: "text",
14997
+ text: `Updated sprint ${doc.frontmatter.id}: ${doc.frontmatter.title}`
14998
+ }
14999
+ ]
15000
+ };
15001
+ }
15002
+ )
15003
+ ];
15004
+ }
15005
+
14659
15006
  // src/plugins/common.ts
14660
15007
  var COMMON_REGISTRATIONS = [
14661
15008
  { type: "meeting", dirName: "meetings", idPrefix: "M" },
14662
15009
  { type: "report", dirName: "reports", idPrefix: "R" },
14663
15010
  { type: "feature", dirName: "features", idPrefix: "F" },
14664
- { type: "epic", dirName: "epics", idPrefix: "E" }
15011
+ { type: "epic", dirName: "epics", idPrefix: "E" },
15012
+ { type: "contribution", dirName: "contributions", idPrefix: "C" },
15013
+ { type: "sprint", dirName: "sprints", idPrefix: "SP" }
14665
15014
  ];
14666
15015
  function createCommonTools(store) {
14667
15016
  return [
14668
15017
  ...createMeetingTools(store),
14669
15018
  ...createReportTools(store),
14670
15019
  ...createFeatureTools(store),
14671
- ...createEpicTools(store)
15020
+ ...createEpicTools(store),
15021
+ ...createContributionTools(store),
15022
+ ...createSprintTools(store)
14672
15023
  ];
14673
15024
  }
14674
15025
 
@@ -14678,7 +15029,7 @@ var genericAgilePlugin = {
14678
15029
  name: "Generic Agile",
14679
15030
  description: "Default methodology plugin providing standard agile governance patterns for decisions, actions, and questions.",
14680
15031
  version: "0.1.0",
14681
- documentTypes: ["decision", "action", "question", "meeting", "report", "feature", "epic"],
15032
+ documentTypes: ["decision", "action", "question", "meeting", "report", "feature", "epic", "contribution", "sprint"],
14682
15033
  documentTypeRegistrations: [...COMMON_REGISTRATIONS],
14683
15034
  tools: (store) => [...createCommonTools(store)],
14684
15035
  promptFragments: {
@@ -14699,7 +15050,16 @@ var genericAgilePlugin = {
14699
15050
  - Create features as "draft" and approve them when requirements are clear and prioritized.
14700
15051
  - Do NOT create epics \u2014 that is the Tech Lead's responsibility. You can view epics to track progress.
14701
15052
  - Use priority levels (critical, high, medium, low) to communicate business value.
14702
- - Tag features for categorization and cross-referencing.`,
15053
+ - Tag features for categorization and cross-referencing.
15054
+
15055
+ **Contribution Tools:**
15056
+ - **list_contributions** / **get_contribution**: Browse and read contribution records.
15057
+ - **create_contribution**: Record a contribution with persona, type, and optional related artifact.
15058
+ - **update_contribution**: Update a contribution (e.g. append effects).
15059
+ - Available contribution types: stakeholder-feedback, acceptance-result, priority-change, market-insight.
15060
+
15061
+ **Sprint Tools (read-only for awareness):**
15062
+ - **list_sprints** / **get_sprint**: View sprints to understand delivery timelines and iteration scope.`,
14703
15063
  "tech-lead": `You own epics and break approved features into implementation work.
14704
15064
 
14705
15065
  **Epic Tools:**
@@ -14720,7 +15080,19 @@ var genericAgilePlugin = {
14720
15080
  - Only create epics against approved features \u2014 create_epic enforces this.
14721
15081
  - Tag work items (actions, decisions, questions) with \`epic:E-xxx\` to group them under an epic.
14722
15082
  - Collaborate with the Delivery Manager on target dates and effort estimates.
14723
- - Each epic should have a clear scope and definition of done.`,
15083
+ - Each epic should have a clear scope and definition of done.
15084
+
15085
+ **Contribution Tools:**
15086
+ - **list_contributions** / **get_contribution**: Browse and read contribution records.
15087
+ - **create_contribution**: Record a contribution with persona, type, and optional related artifact.
15088
+ - **update_contribution**: Update a contribution (e.g. append effects).
15089
+ - Available contribution types: action-result, spike-findings, technical-assessment, architecture-review.
15090
+
15091
+ **Sprint Tools:**
15092
+ - **list_sprints** / **get_sprint**: View sprints to understand iteration scope and delivery dates.
15093
+ - **update_sprint**: Assign epics to sprints by updating linkedEpics when breaking features into work.
15094
+ - Tag technical actions and decisions with \`sprint:SP-xxx\` to associate them with a sprint.
15095
+ - Use **generate_sprint_progress** to track technical work completion within an iteration.`,
14724
15096
  "delivery-manager": `You track delivery across features and epics, manage schedules, and report on progress.
14725
15097
 
14726
15098
  **Report Tools:**
@@ -14748,27 +15120,53 @@ var genericAgilePlugin = {
14748
15120
  - After generating any report, offer to save it with save_report for audit trail.
14749
15121
  - Proactively flag risks: unowned actions, overdue items, epics linked to deferred features.
14750
15122
  - Use feature progress reports for stakeholder updates and epic progress for sprint-level tracking.
14751
- - Use analyze_meeting after meetings to extract outcomes into governance artifacts.`,
14752
- "*": `You have access to feature, epic, and meeting tools for project coordination:
15123
+ - Use analyze_meeting after meetings to extract outcomes into governance artifacts.
15124
+
15125
+ **Contribution Tools:**
15126
+ - **list_contributions** / **get_contribution**: Browse and read contribution records.
15127
+ - **create_contribution**: Record a contribution with persona, type, and optional related artifact.
15128
+ - **update_contribution**: Update a contribution (e.g. append effects).
15129
+ - Available contribution types: risk-finding, blocker-report, dependency-update, status-assessment.
15130
+
15131
+ **Sprint Tools:**
15132
+ - **list_sprints** / **get_sprint**: Browse and read sprint definitions.
15133
+ - **create_sprint**: Create sprints with dates, goals, and linked epics. Use status "planned" for upcoming sprints or "active"/"completed"/"cancelled" for current/past sprints.
15134
+ - **update_sprint**: Update sprint status, dates, goal, or linked epics. When linkedEpics changes, affected epics are re-tagged automatically.
15135
+ - **generate_sprint_progress**: Progress report for a specific sprint or all sprints \u2014 shows linked epics with statuses, work items tagged \`sprint:SP-xxx\` grouped by status, and done/total completion %.
15136
+ - Use \`save_report\` with reportType "sprint-progress" to persist sprint reports.
15137
+
15138
+ **Sprint Workflow:**
15139
+ - Create sprints with clear goals and date boundaries.
15140
+ - Assign epics to sprints via linkedEpics.
15141
+ - Tag work items (actions, decisions, questions) with \`sprint:SP-xxx\` for sprint scoping.
15142
+ - Track delivery dates and flag at-risk sprints.
15143
+ - Register past/completed sprints for historical tracking.`,
15144
+ "*": `You have access to feature, epic, sprint, and meeting tools for project coordination:
14753
15145
 
14754
15146
  **Features** (F-xxx): Product capabilities defined by the Product Owner. Features progress through draft \u2192 approved \u2192 done.
14755
15147
  **Epics** (E-xxx): Implementation work packages created by the Tech Lead, linked to approved features. Epics progress through planned \u2192 in-progress \u2192 done.
15148
+ **Sprints** (SP-xxx): Time-boxed iterations that group epics and work items with delivery dates. Sprints progress through planned \u2192 active \u2192 completed (or cancelled).
14756
15149
  **Meetings**: Meeting records with attendees, agendas, and notes.
14757
15150
 
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.
15151
+ **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, the Delivery Manager plans sprints and tracks dates and progress. Work items are associated with sprints via \`sprint:SP-xxx\` tags.
14759
15152
 
14760
15153
  - **list_meetings** / **get_meeting**: Browse and read meeting records.
14761
15154
  - **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
15155
  - **update_meeting**: Update meeting status or notes.
14763
- - **analyze_meeting**: Analyze a meeting to extract decisions, actions, and questions as governance artifacts.`
15156
+ - **analyze_meeting**: Analyze a meeting to extract decisions, actions, and questions as governance artifacts.
15157
+
15158
+ **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.
15159
+ - **list_contributions** / **get_contribution**: Browse and read contribution records.
15160
+ - **create_contribution**: Record a contribution with persona, type, and optional related artifact.
15161
+ - **update_contribution**: Update a contribution (e.g. append effects).`
14764
15162
  }
14765
15163
  };
14766
15164
 
14767
15165
  // src/plugins/builtin/tools/use-cases.ts
14768
- import { tool as tool5 } from "@anthropic-ai/claude-agent-sdk";
15166
+ import { tool as tool7 } from "@anthropic-ai/claude-agent-sdk";
14769
15167
  function createUseCaseTools(store) {
14770
15168
  return [
14771
- tool5(
15169
+ tool7(
14772
15170
  "list_use_cases",
14773
15171
  "List all extension use cases, optionally filtered by status or extension type",
14774
15172
  {
@@ -14798,7 +15196,7 @@ function createUseCaseTools(store) {
14798
15196
  },
14799
15197
  { annotations: { readOnly: true } }
14800
15198
  ),
14801
- tool5(
15199
+ tool7(
14802
15200
  "get_use_case",
14803
15201
  "Get the full content of a specific use case by ID",
14804
15202
  { id: external_exports.string().describe("Use case ID (e.g. 'UC-001')") },
@@ -14825,7 +15223,7 @@ function createUseCaseTools(store) {
14825
15223
  },
14826
15224
  { annotations: { readOnly: true } }
14827
15225
  ),
14828
- tool5(
15226
+ tool7(
14829
15227
  "create_use_case",
14830
15228
  "Create a new extension use case definition (Phase 1: Assess Extension Use Case)",
14831
15229
  {
@@ -14859,7 +15257,7 @@ function createUseCaseTools(store) {
14859
15257
  };
14860
15258
  }
14861
15259
  ),
14862
- tool5(
15260
+ tool7(
14863
15261
  "update_use_case",
14864
15262
  "Update an existing extension use case",
14865
15263
  {
@@ -14889,10 +15287,10 @@ function createUseCaseTools(store) {
14889
15287
  }
14890
15288
 
14891
15289
  // src/plugins/builtin/tools/tech-assessments.ts
14892
- import { tool as tool6 } from "@anthropic-ai/claude-agent-sdk";
15290
+ import { tool as tool8 } from "@anthropic-ai/claude-agent-sdk";
14893
15291
  function createTechAssessmentTools(store) {
14894
15292
  return [
14895
- tool6(
15293
+ tool8(
14896
15294
  "list_tech_assessments",
14897
15295
  "List all technology assessments, optionally filtered by status",
14898
15296
  {
@@ -14923,7 +15321,7 @@ function createTechAssessmentTools(store) {
14923
15321
  },
14924
15322
  { annotations: { readOnly: true } }
14925
15323
  ),
14926
- tool6(
15324
+ tool8(
14927
15325
  "get_tech_assessment",
14928
15326
  "Get the full content of a specific technology assessment by ID",
14929
15327
  { id: external_exports.string().describe("Tech assessment ID (e.g. 'TA-001')") },
@@ -14950,7 +15348,7 @@ function createTechAssessmentTools(store) {
14950
15348
  },
14951
15349
  { annotations: { readOnly: true } }
14952
15350
  ),
14953
- tool6(
15351
+ tool8(
14954
15352
  "create_tech_assessment",
14955
15353
  "Create a new technology assessment linked to an assessed/approved use case (Phase 2: Assess Extension Technology)",
14956
15354
  {
@@ -15021,7 +15419,7 @@ function createTechAssessmentTools(store) {
15021
15419
  };
15022
15420
  }
15023
15421
  ),
15024
- tool6(
15422
+ tool8(
15025
15423
  "update_tech_assessment",
15026
15424
  "Update an existing technology assessment. The linked use case cannot be changed.",
15027
15425
  {
@@ -15051,10 +15449,10 @@ function createTechAssessmentTools(store) {
15051
15449
  }
15052
15450
 
15053
15451
  // src/plugins/builtin/tools/extension-designs.ts
15054
- import { tool as tool7 } from "@anthropic-ai/claude-agent-sdk";
15452
+ import { tool as tool9 } from "@anthropic-ai/claude-agent-sdk";
15055
15453
  function createExtensionDesignTools(store) {
15056
15454
  return [
15057
- tool7(
15455
+ tool9(
15058
15456
  "list_extension_designs",
15059
15457
  "List all extension designs, optionally filtered by status",
15060
15458
  {
@@ -15084,7 +15482,7 @@ function createExtensionDesignTools(store) {
15084
15482
  },
15085
15483
  { annotations: { readOnly: true } }
15086
15484
  ),
15087
- tool7(
15485
+ tool9(
15088
15486
  "get_extension_design",
15089
15487
  "Get the full content of a specific extension design by ID",
15090
15488
  { id: external_exports.string().describe("Extension design ID (e.g. 'XD-001')") },
@@ -15111,7 +15509,7 @@ function createExtensionDesignTools(store) {
15111
15509
  },
15112
15510
  { annotations: { readOnly: true } }
15113
15511
  ),
15114
- tool7(
15512
+ tool9(
15115
15513
  "create_extension_design",
15116
15514
  "Create a new extension design linked to a recommended tech assessment (Phase 3: Define Extension Target Solution)",
15117
15515
  {
@@ -15179,7 +15577,7 @@ function createExtensionDesignTools(store) {
15179
15577
  };
15180
15578
  }
15181
15579
  ),
15182
- tool7(
15580
+ tool9(
15183
15581
  "update_extension_design",
15184
15582
  "Update an existing extension design. The linked tech assessment cannot be changed.",
15185
15583
  {
@@ -15208,10 +15606,10 @@ function createExtensionDesignTools(store) {
15208
15606
  }
15209
15607
 
15210
15608
  // src/plugins/builtin/tools/aem-reports.ts
15211
- import { tool as tool8 } from "@anthropic-ai/claude-agent-sdk";
15609
+ import { tool as tool10 } from "@anthropic-ai/claude-agent-sdk";
15212
15610
  function createAemReportTools(store) {
15213
15611
  return [
15214
- tool8(
15612
+ tool10(
15215
15613
  "generate_extension_portfolio",
15216
15614
  "Generate a portfolio view of all use cases with their linked tech assessments and extension designs",
15217
15615
  {},
@@ -15263,7 +15661,7 @@ function createAemReportTools(store) {
15263
15661
  },
15264
15662
  { annotations: { readOnly: true } }
15265
15663
  ),
15266
- tool8(
15664
+ tool10(
15267
15665
  "generate_tech_readiness",
15268
15666
  "Generate a BTP technology readiness report showing service coverage and gaps across assessments",
15269
15667
  {},
@@ -15315,7 +15713,7 @@ function createAemReportTools(store) {
15315
15713
  },
15316
15714
  { annotations: { readOnly: true } }
15317
15715
  ),
15318
- tool8(
15716
+ tool10(
15319
15717
  "generate_phase_status",
15320
15718
  "Generate a phase progress report showing artifact counts and readiness per AEM phase",
15321
15719
  {},
@@ -15377,11 +15775,11 @@ function createAemReportTools(store) {
15377
15775
  import * as fs3 from "fs";
15378
15776
  import * as path3 from "path";
15379
15777
  import * as YAML2 from "yaml";
15380
- import { tool as tool9 } from "@anthropic-ai/claude-agent-sdk";
15778
+ import { tool as tool11 } from "@anthropic-ai/claude-agent-sdk";
15381
15779
  var PHASES = ["assess-use-case", "assess-technology", "define-solution"];
15382
15780
  function createAemPhaseTools(store, marvinDir) {
15383
15781
  return [
15384
- tool9(
15782
+ tool11(
15385
15783
  "get_current_phase",
15386
15784
  "Get the current AEM phase from project configuration",
15387
15785
  {},
@@ -15402,7 +15800,7 @@ function createAemPhaseTools(store, marvinDir) {
15402
15800
  },
15403
15801
  { annotations: { readOnly: true } }
15404
15802
  ),
15405
- tool9(
15803
+ tool11(
15406
15804
  "advance_phase",
15407
15805
  "Advance to the next AEM phase. Performs soft gate checks and warns if artifacts are incomplete, but does not block.",
15408
15806
  {
@@ -15813,7 +16211,8 @@ var productOwner = {
15813
16211
  "Acceptance criteria",
15814
16212
  "Feature definition and prioritization"
15815
16213
  ],
15816
- documentTypes: ["decision", "question", "action", "feature"]
16214
+ documentTypes: ["decision", "question", "action", "feature"],
16215
+ contributionTypes: ["stakeholder-feedback", "acceptance-result", "priority-change", "market-insight"]
15817
16216
  };
15818
16217
 
15819
16218
  // src/personas/builtin/delivery-manager.ts
@@ -15849,9 +16248,11 @@ var deliveryManager = {
15849
16248
  "Team coordination",
15850
16249
  "Process governance",
15851
16250
  "Status tracking",
15852
- "Epic scheduling and tracking"
16251
+ "Epic scheduling and tracking",
16252
+ "Sprint planning and tracking"
15853
16253
  ],
15854
- documentTypes: ["action", "decision", "meeting", "question", "feature", "epic"]
16254
+ documentTypes: ["action", "decision", "meeting", "question", "feature", "epic", "sprint"],
16255
+ contributionTypes: ["risk-finding", "blocker-report", "dependency-update", "status-assessment"]
15855
16256
  };
15856
16257
 
15857
16258
  // src/personas/builtin/tech-lead.ts
@@ -15887,9 +16288,11 @@ var techLead = {
15887
16288
  "Technical decisions",
15888
16289
  "Implementation guidance",
15889
16290
  "Non-functional requirements",
15890
- "Epic creation and scoping"
16291
+ "Epic creation and scoping",
16292
+ "Sprint scoping and technical execution"
15891
16293
  ],
15892
- documentTypes: ["decision", "action", "question", "epic"]
16294
+ documentTypes: ["decision", "action", "question", "epic", "sprint"],
16295
+ contributionTypes: ["action-result", "spike-findings", "technical-assessment", "architecture-review"]
15893
16296
  };
15894
16297
 
15895
16298
  // src/personas/registry.ts
@@ -15947,6 +16350,7 @@ You have access to governance tools for managing project artifacts:
15947
16350
  - **Questions** (Q-xxx): List, get, create, and update questions
15948
16351
  - **Features** (F-xxx): List, get, create, and update feature definitions
15949
16352
  - **Epics** (E-xxx): List, get, create, and update implementation epics (must link to approved features)
16353
+ - **Sprints** (SP-xxx): List, get, create, and update time-boxed iterations with linked epics and delivery dates
15950
16354
  - **Documents**: Search and read any project document
15951
16355
  - **Sources**: List source documents and view their processing status and derived artifacts
15952
16356
 
@@ -16046,9 +16450,9 @@ var DocumentStore = class {
16046
16450
  }
16047
16451
  }
16048
16452
  }
16049
- list(query6) {
16453
+ list(query7) {
16050
16454
  const results = [];
16051
- const types = query6?.type ? [query6.type] : Object.keys(this.typeDirs);
16455
+ const types = query7?.type ? [query7.type] : Object.keys(this.typeDirs);
16052
16456
  for (const type of types) {
16053
16457
  const dirName = this.typeDirs[type];
16054
16458
  if (!dirName) continue;
@@ -16059,9 +16463,9 @@ var DocumentStore = class {
16059
16463
  const filePath = path5.join(dir, file2);
16060
16464
  const raw = fs5.readFileSync(filePath, "utf-8");
16061
16465
  const doc = parseDocument(raw, filePath);
16062
- if (query6?.status && doc.frontmatter.status !== query6.status) continue;
16063
- if (query6?.owner && doc.frontmatter.owner !== query6.owner) continue;
16064
- if (query6?.tag && (!doc.frontmatter.tags || !doc.frontmatter.tags.includes(query6.tag)))
16466
+ if (query7?.status && doc.frontmatter.status !== query7.status) continue;
16467
+ if (query7?.owner && doc.frontmatter.owner !== query7.owner) continue;
16468
+ if (query7?.tag && (!doc.frontmatter.tags || !doc.frontmatter.tags.includes(query7.tag)))
16065
16469
  continue;
16066
16470
  results.push(doc);
16067
16471
  }
@@ -16264,10 +16668,10 @@ import {
16264
16668
  } from "@anthropic-ai/claude-agent-sdk";
16265
16669
 
16266
16670
  // src/agent/tools/decisions.ts
16267
- import { tool as tool10 } from "@anthropic-ai/claude-agent-sdk";
16671
+ import { tool as tool12 } from "@anthropic-ai/claude-agent-sdk";
16268
16672
  function createDecisionTools(store) {
16269
16673
  return [
16270
- tool10(
16674
+ tool12(
16271
16675
  "list_decisions",
16272
16676
  "List all decisions in the project, optionally filtered by status",
16273
16677
  { status: external_exports.string().optional().describe("Filter by status (e.g. 'open', 'decided', 'superseded')") },
@@ -16285,7 +16689,7 @@ function createDecisionTools(store) {
16285
16689
  },
16286
16690
  { annotations: { readOnly: true } }
16287
16691
  ),
16288
- tool10(
16692
+ tool12(
16289
16693
  "get_decision",
16290
16694
  "Get the full content of a specific decision by ID",
16291
16695
  { id: external_exports.string().describe("Decision ID (e.g. 'D-001')") },
@@ -16312,7 +16716,7 @@ function createDecisionTools(store) {
16312
16716
  },
16313
16717
  { annotations: { readOnly: true } }
16314
16718
  ),
16315
- tool10(
16719
+ tool12(
16316
16720
  "create_decision",
16317
16721
  "Create a new decision record",
16318
16722
  {
@@ -16343,7 +16747,7 @@ function createDecisionTools(store) {
16343
16747
  };
16344
16748
  }
16345
16749
  ),
16346
- tool10(
16750
+ tool12(
16347
16751
  "update_decision",
16348
16752
  "Update an existing decision",
16349
16753
  {
@@ -16370,10 +16774,10 @@ function createDecisionTools(store) {
16370
16774
  }
16371
16775
 
16372
16776
  // src/agent/tools/actions.ts
16373
- import { tool as tool11 } from "@anthropic-ai/claude-agent-sdk";
16777
+ import { tool as tool13 } from "@anthropic-ai/claude-agent-sdk";
16374
16778
  function createActionTools(store) {
16375
16779
  return [
16376
- tool11(
16780
+ tool13(
16377
16781
  "list_actions",
16378
16782
  "List all action items in the project, optionally filtered by status or owner",
16379
16783
  {
@@ -16400,7 +16804,7 @@ function createActionTools(store) {
16400
16804
  },
16401
16805
  { annotations: { readOnly: true } }
16402
16806
  ),
16403
- tool11(
16807
+ tool13(
16404
16808
  "get_action",
16405
16809
  "Get the full content of a specific action item by ID",
16406
16810
  { id: external_exports.string().describe("Action ID (e.g. 'A-001')") },
@@ -16427,7 +16831,7 @@ function createActionTools(store) {
16427
16831
  },
16428
16832
  { annotations: { readOnly: true } }
16429
16833
  ),
16430
- tool11(
16834
+ tool13(
16431
16835
  "create_action",
16432
16836
  "Create a new action item",
16433
16837
  {
@@ -16460,7 +16864,7 @@ function createActionTools(store) {
16460
16864
  };
16461
16865
  }
16462
16866
  ),
16463
- tool11(
16867
+ tool13(
16464
16868
  "update_action",
16465
16869
  "Update an existing action item",
16466
16870
  {
@@ -16488,10 +16892,10 @@ function createActionTools(store) {
16488
16892
  }
16489
16893
 
16490
16894
  // src/agent/tools/questions.ts
16491
- import { tool as tool12 } from "@anthropic-ai/claude-agent-sdk";
16895
+ import { tool as tool14 } from "@anthropic-ai/claude-agent-sdk";
16492
16896
  function createQuestionTools(store) {
16493
16897
  return [
16494
- tool12(
16898
+ tool14(
16495
16899
  "list_questions",
16496
16900
  "List all questions in the project, optionally filtered by status",
16497
16901
  {
@@ -16512,7 +16916,7 @@ function createQuestionTools(store) {
16512
16916
  },
16513
16917
  { annotations: { readOnly: true } }
16514
16918
  ),
16515
- tool12(
16919
+ tool14(
16516
16920
  "get_question",
16517
16921
  "Get the full content of a specific question by ID",
16518
16922
  { id: external_exports.string().describe("Question ID (e.g. 'Q-001')") },
@@ -16539,7 +16943,7 @@ function createQuestionTools(store) {
16539
16943
  },
16540
16944
  { annotations: { readOnly: true } }
16541
16945
  ),
16542
- tool12(
16946
+ tool14(
16543
16947
  "create_question",
16544
16948
  "Create a new question that needs to be answered",
16545
16949
  {
@@ -16570,7 +16974,7 @@ function createQuestionTools(store) {
16570
16974
  };
16571
16975
  }
16572
16976
  ),
16573
- tool12(
16977
+ tool14(
16574
16978
  "update_question",
16575
16979
  "Update an existing question",
16576
16980
  {
@@ -16597,10 +17001,10 @@ function createQuestionTools(store) {
16597
17001
  }
16598
17002
 
16599
17003
  // src/agent/tools/documents.ts
16600
- import { tool as tool13 } from "@anthropic-ai/claude-agent-sdk";
17004
+ import { tool as tool15 } from "@anthropic-ai/claude-agent-sdk";
16601
17005
  function createDocumentTools(store) {
16602
17006
  return [
16603
- tool13(
17007
+ tool15(
16604
17008
  "search_documents",
16605
17009
  "Search all project documents, optionally filtered by type, status, or tag",
16606
17010
  {
@@ -16632,7 +17036,7 @@ function createDocumentTools(store) {
16632
17036
  },
16633
17037
  { annotations: { readOnly: true } }
16634
17038
  ),
16635
- tool13(
17039
+ tool15(
16636
17040
  "read_document",
16637
17041
  "Read the full content of any project document by ID",
16638
17042
  { id: external_exports.string().describe("Document ID (e.g. 'D-001', 'A-003', 'Q-002')") },
@@ -16659,7 +17063,7 @@ function createDocumentTools(store) {
16659
17063
  },
16660
17064
  { annotations: { readOnly: true } }
16661
17065
  ),
16662
- tool13(
17066
+ tool15(
16663
17067
  "project_summary",
16664
17068
  "Get a summary of all project documents and their counts",
16665
17069
  {},
@@ -16691,10 +17095,10 @@ function createDocumentTools(store) {
16691
17095
  }
16692
17096
 
16693
17097
  // src/agent/tools/sources.ts
16694
- import { tool as tool14 } from "@anthropic-ai/claude-agent-sdk";
17098
+ import { tool as tool16 } from "@anthropic-ai/claude-agent-sdk";
16695
17099
  function createSourceTools(manifest) {
16696
17100
  return [
16697
- tool14(
17101
+ tool16(
16698
17102
  "list_sources",
16699
17103
  "List all source documents and their processing status",
16700
17104
  {
@@ -16724,7 +17128,7 @@ function createSourceTools(manifest) {
16724
17128
  },
16725
17129
  { annotations: { readOnly: true } }
16726
17130
  ),
16727
- tool14(
17131
+ tool16(
16728
17132
  "get_source_info",
16729
17133
  "Get detailed information about a specific source document",
16730
17134
  {
@@ -16762,10 +17166,10 @@ function createSourceTools(manifest) {
16762
17166
  }
16763
17167
 
16764
17168
  // src/agent/tools/sessions.ts
16765
- import { tool as tool15 } from "@anthropic-ai/claude-agent-sdk";
17169
+ import { tool as tool17 } from "@anthropic-ai/claude-agent-sdk";
16766
17170
  function createSessionTools(store) {
16767
17171
  return [
16768
- tool15(
17172
+ tool17(
16769
17173
  "list_sessions",
16770
17174
  "List all saved chat sessions, sorted by most recently used",
16771
17175
  {
@@ -16789,7 +17193,7 @@ function createSessionTools(store) {
16789
17193
  },
16790
17194
  { annotations: { readOnly: true } }
16791
17195
  ),
16792
- tool15(
17196
+ tool17(
16793
17197
  "get_session",
16794
17198
  "Get details of a specific saved session by name",
16795
17199
  { name: external_exports.string().describe("Session name (e.g. 'jwt-auth-decision')") },
@@ -18378,7 +18782,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18378
18782
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
18379
18783
 
18380
18784
  // src/skills/action-tools.ts
18381
- import { tool as tool16 } from "@anthropic-ai/claude-agent-sdk";
18785
+ import { tool as tool18 } from "@anthropic-ai/claude-agent-sdk";
18382
18786
 
18383
18787
  // src/skills/action-runner.ts
18384
18788
  import { query as query4 } from "@anthropic-ai/claude-agent-sdk";
@@ -18444,7 +18848,7 @@ function createSkillActionTools(skills, context) {
18444
18848
  if (!skill.actions) continue;
18445
18849
  for (const action of skill.actions) {
18446
18850
  tools.push(
18447
- tool16(
18851
+ tool18(
18448
18852
  `${skill.id}__${action.id}`,
18449
18853
  action.description,
18450
18854
  {
@@ -18536,10 +18940,10 @@ ${lines.join("\n\n")}`;
18536
18940
  }
18537
18941
 
18538
18942
  // src/mcp/persona-tools.ts
18539
- import { tool as tool17 } from "@anthropic-ai/claude-agent-sdk";
18943
+ import { tool as tool19 } from "@anthropic-ai/claude-agent-sdk";
18540
18944
  function createPersonaTools(ctx, marvinDir) {
18541
18945
  return [
18542
- tool17(
18946
+ tool19(
18543
18947
  "set_persona",
18544
18948
  "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
18949
  {
@@ -18569,7 +18973,7 @@ ${summaries}`
18569
18973
  };
18570
18974
  }
18571
18975
  ),
18572
- tool17(
18976
+ tool19(
18573
18977
  "get_persona_guidance",
18574
18978
  "Get guidance for a persona without changing the active persona. If no persona is specified, lists all available personas with summaries.",
18575
18979
  {
@@ -19184,7 +19588,8 @@ function getDirNameForType(store, type) {
19184
19588
  meeting: "meetings",
19185
19589
  report: "reports",
19186
19590
  feature: "features",
19187
- epic: "epics"
19591
+ epic: "epics",
19592
+ sprint: "sprints"
19188
19593
  };
19189
19594
  return typeDir[type] ?? `${type}s`;
19190
19595
  }
@@ -19681,12 +20086,417 @@ Analyzing meeting: ${meetingDoc.frontmatter.title}`));
19681
20086
  });
19682
20087
  }
19683
20088
 
20089
+ // src/cli/commands/contribute.ts
20090
+ import chalk15 from "chalk";
20091
+
20092
+ // src/contributions/contribute.ts
20093
+ import chalk14 from "chalk";
20094
+ import ora5 from "ora";
20095
+ import { query as query6 } from "@anthropic-ai/claude-agent-sdk";
20096
+
20097
+ // src/contributions/prompts.ts
20098
+ function buildContributeSystemPrompt(persona, contributionType, projectConfig, isDraft) {
20099
+ const parts = [];
20100
+ parts.push(persona.systemPrompt);
20101
+ parts.push(`
20102
+ ## Project Context
20103
+ - **Project Name:** ${projectConfig.name}
20104
+ - **Methodology:** ${projectConfig.methodology ?? "Generic Agile"}
20105
+ `);
20106
+ parts.push(`
20107
+ ## Contribution Analysis Task
20108
+ You are processing a **${contributionType}** contribution from the **${persona.name}**.
20109
+ Review the contribution content and determine what governance effects should follow.
20110
+
20111
+ Possible effects include:
20112
+ 1. **Create** new artifacts \u2014 decisions (D-xxx), actions (A-xxx), questions (Q-xxx)
20113
+ 2. **Update** existing artifacts \u2014 change status, add information, close items
20114
+ `);
20115
+ parts.push(buildContributionTypeInstructions(contributionType));
20116
+ if (isDraft) {
20117
+ parts.push(`
20118
+ ## Mode: Draft Proposal
20119
+ Present your findings as a structured proposal. Do NOT create or update any artifacts.
20120
+ Format your response as:
20121
+
20122
+ ### Proposed Effects
20123
+ For each effect:
20124
+ - **Action:** [create/update]
20125
+ - **Target:** [new artifact type or existing artifact ID]
20126
+ - **Details:** [what would be created or changed]
20127
+ - **Rationale:** [why this effect follows from the contribution]
20128
+
20129
+ ### Summary
20130
+ Provide a brief summary of the contribution analysis and recommendations.
20131
+ `);
20132
+ } else {
20133
+ parts.push(`
20134
+ ## Mode: Direct Execution
20135
+ Use the MCP tools to create and update artifacts directly:
20136
+ - Use \`create_decision\` / \`create_action\` / \`create_question\` for new artifacts
20137
+ - Use \`update_decision\` / \`update_action\` / \`update_question\` to modify existing ones
20138
+ - Use \`list_decisions\` / \`list_actions\` / \`list_questions\` to check existing artifacts before creating or updating
20139
+
20140
+ Before creating artifacts, check existing ones using the list tools to avoid duplicates.
20141
+
20142
+ For EVERY artifact you create or update, include:
20143
+ - A \`source:<contribution-id>\` tag in the tags array (the contribution ID is provided in the user prompt)
20144
+ - Clear title and detailed content with context from the contribution
20145
+
20146
+ After processing all effects, provide a summary of what was created and updated.
20147
+ `);
20148
+ }
20149
+ return parts.join("\n");
20150
+ }
20151
+ function buildContributionTypeInstructions(contributionType) {
20152
+ const instructions = {
20153
+ "action-result": `
20154
+ ### Type-Specific Guidance: Action Result
20155
+ The contributor is reporting results of a completed action.
20156
+ - Update the referenced action's status to "done" if results are conclusive
20157
+ - Check if the results answer any open questions \u2014 if so, update those questions
20158
+ - Check if results challenge any existing decisions \u2014 if so, create a new question or update the decision
20159
+ - If results reveal new work, create new actions`,
20160
+ "spike-findings": `
20161
+ ### Type-Specific Guidance: Spike Findings
20162
+ The contributor is sharing findings from a technical investigation (spike).
20163
+ - Create decisions for any architectural or technical choices made
20164
+ - Update or close the original spike action if referenced
20165
+ - Create new actions for follow-up implementation work
20166
+ - Create questions for areas needing further investigation`,
20167
+ "technical-assessment": `
20168
+ ### Type-Specific Guidance: Technical Assessment
20169
+ The contributor is providing a technical evaluation.
20170
+ - Create decisions for technical recommendations
20171
+ - Create actions for remediation or improvement tasks
20172
+ - Create questions for areas needing more investigation
20173
+ - Flag any risks by tagging artifacts with "risk"`,
20174
+ "architecture-review": `
20175
+ ### Type-Specific Guidance: Architecture Review
20176
+ The contributor is documenting an architecture review.
20177
+ - Create decisions for architectural choices and trade-offs
20178
+ - Create actions for any architectural changes needed
20179
+ - Create questions for design concerns or alternatives to evaluate
20180
+ - Update existing decisions if the review validates or challenges them`,
20181
+ "stakeholder-feedback": `
20182
+ ### Type-Specific Guidance: Stakeholder Feedback
20183
+ The contributor is relaying feedback from stakeholders.
20184
+ - Create or update decisions based on stakeholder direction
20185
+ - Create actions for requested changes or follow-ups
20186
+ - Create questions for items needing clarification
20187
+ - Update priorities on existing artifacts if feedback changes priorities`,
20188
+ "acceptance-result": `
20189
+ ### Type-Specific Guidance: Acceptance Result
20190
+ The contributor is reporting results of acceptance testing.
20191
+ - Update the referenced action/feature status based on pass/fail
20192
+ - Create new actions for any rework needed
20193
+ - Create questions for ambiguous acceptance criteria
20194
+ - If accepted, mark related actions as "done"`,
20195
+ "priority-change": `
20196
+ ### Type-Specific Guidance: Priority Change
20197
+ The contributor is communicating a change in priorities.
20198
+ - Update priority fields on affected artifacts
20199
+ - Create decisions documenting the priority change rationale
20200
+ - Create actions for any re-planning needed
20201
+ - Flag any blocked or impacted items`,
20202
+ "market-insight": `
20203
+ ### Type-Specific Guidance: Market Insight
20204
+ The contributor is sharing market intelligence or competitive information.
20205
+ - Create decisions if the insight drives product direction changes
20206
+ - Create questions for areas needing further market research
20207
+ - Create actions for competitive response tasks
20208
+ - Update existing features or priorities if affected`,
20209
+ "risk-finding": `
20210
+ ### Type-Specific Guidance: Risk Finding
20211
+ The contributor is identifying a project risk.
20212
+ - Create actions for risk mitigation tasks
20213
+ - Create decisions for risk response strategies
20214
+ - Create questions for risks needing further assessment
20215
+ - Tag all related artifacts with "risk" for tracking`,
20216
+ "blocker-report": `
20217
+ ### Type-Specific Guidance: Blocker Report
20218
+ The contributor is reporting a blocker.
20219
+ - Create actions to resolve the blocker
20220
+ - Update blocked artifacts' status or add blocking context
20221
+ - Create questions for blockers needing external input
20222
+ - Escalate by creating high-priority actions if critical`,
20223
+ "dependency-update": `
20224
+ ### Type-Specific Guidance: Dependency Update
20225
+ The contributor is reporting on external or internal dependencies.
20226
+ - Update affected actions with dependency information
20227
+ - Create new actions for dependency-related tasks
20228
+ - Create questions for uncertain dependencies
20229
+ - Update timelines or flag schedule risks`,
20230
+ "status-assessment": `
20231
+ ### Type-Specific Guidance: Status Assessment
20232
+ The contributor is providing a delivery status assessment.
20233
+ - Update action statuses based on assessment
20234
+ - Create actions for corrective measures
20235
+ - Create questions for items needing investigation
20236
+ - Create decisions for any process changes needed`
20237
+ };
20238
+ return instructions[contributionType] ?? `
20239
+ ### Type-Specific Guidance
20240
+ Analyze the contribution and determine appropriate governance effects based on the content.`;
20241
+ }
20242
+ function buildContributeUserPrompt(contributionId, contributionType, content, aboutArtifact, isDraft) {
20243
+ const mode = isDraft ? "propose" : "execute";
20244
+ const aboutLine = aboutArtifact ? `
20245
+ **Related Artifact:** ${aboutArtifact}` : "";
20246
+ return `Please analyze the following contribution and ${mode} governance effects.
20247
+
20248
+ **Contribution ID:** ${contributionId}
20249
+ **Contribution Type:** ${contributionType}${aboutLine}
20250
+
20251
+ ---
20252
+ ${content}
20253
+ ---
20254
+
20255
+ 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.`;
20256
+ }
20257
+
20258
+ // src/contributions/contribute.ts
20259
+ async function contributeFromPersona(options) {
20260
+ const { marvinDir, prompt, aboutArtifact, draft } = options;
20261
+ const config2 = getConfig(marvinDir);
20262
+ const personaId = resolvePersonaId(options.persona);
20263
+ const persona = getPersona(personaId);
20264
+ const allowedTypes = persona.contributionTypes ?? [];
20265
+ if (allowedTypes.length > 0 && !allowedTypes.includes(options.contributionType)) {
20266
+ throw new Error(
20267
+ `Contribution type "${options.contributionType}" is not valid for persona "${persona.name}". Allowed types: ${allowedTypes.join(", ")}`
20268
+ );
20269
+ }
20270
+ const plugin = resolvePlugin(config2.project.methodology);
20271
+ const registrations = plugin?.documentTypeRegistrations ?? [];
20272
+ const store = new DocumentStore(marvinDir, registrations);
20273
+ const contributionFrontmatter = {
20274
+ title: `${options.contributionType}: ${prompt.slice(0, 60)}${prompt.length > 60 ? "..." : ""}`,
20275
+ status: "open",
20276
+ persona: personaId,
20277
+ contributionType: options.contributionType
20278
+ };
20279
+ if (aboutArtifact) contributionFrontmatter.aboutArtifact = aboutArtifact;
20280
+ const contributionDoc = store.create("contribution", contributionFrontmatter, prompt);
20281
+ const contributionId = contributionDoc.frontmatter.id;
20282
+ const createdArtifacts = [];
20283
+ const updatedArtifacts = [];
20284
+ if (!draft) {
20285
+ const originalCreate = store.create.bind(store);
20286
+ store.create = (type, frontmatter, content) => {
20287
+ const tags = frontmatter.tags ?? [];
20288
+ const sourceTag = `source:${contributionId}`;
20289
+ if (!tags.includes(sourceTag)) {
20290
+ tags.push(sourceTag);
20291
+ }
20292
+ const doc = originalCreate(type, { ...frontmatter, source: contributionId, tags }, content);
20293
+ createdArtifacts.push(doc.frontmatter.id);
20294
+ return doc;
20295
+ };
20296
+ const originalUpdate = store.update.bind(store);
20297
+ store.update = (id, updates, content) => {
20298
+ if (id === contributionId) {
20299
+ return originalUpdate(id, updates, content);
20300
+ }
20301
+ const existing = store.get(id);
20302
+ const existingTags = existing?.frontmatter.tags ?? [];
20303
+ const sourceTag = `source:${contributionId}`;
20304
+ if (!existingTags.includes(sourceTag)) {
20305
+ existingTags.push(sourceTag);
20306
+ }
20307
+ const doc = originalUpdate(id, { ...updates, tags: existingTags }, content);
20308
+ if (!updatedArtifacts.includes(id)) {
20309
+ updatedArtifacts.push(id);
20310
+ }
20311
+ return doc;
20312
+ };
20313
+ }
20314
+ const pluginTools = plugin ? getPluginTools(plugin, store, marvinDir) : [];
20315
+ const mcpServer = createMarvinMcpServer(store, { pluginTools });
20316
+ const systemPrompt = buildContributeSystemPrompt(persona, options.contributionType, config2.project, draft);
20317
+ const userPrompt = buildContributeUserPrompt(
20318
+ contributionId,
20319
+ options.contributionType,
20320
+ prompt,
20321
+ aboutArtifact,
20322
+ draft
20323
+ );
20324
+ const spinner = ora5({ text: `Processing contribution ${contributionId}...`, color: "cyan" });
20325
+ spinner.start();
20326
+ try {
20327
+ const allowedTools = draft ? [
20328
+ "mcp__marvin-governance__list_decisions",
20329
+ "mcp__marvin-governance__list_actions",
20330
+ "mcp__marvin-governance__list_questions",
20331
+ "mcp__marvin-governance__get_decision",
20332
+ "mcp__marvin-governance__get_action",
20333
+ "mcp__marvin-governance__get_question"
20334
+ ] : [
20335
+ "mcp__marvin-governance__create_decision",
20336
+ "mcp__marvin-governance__create_action",
20337
+ "mcp__marvin-governance__create_question",
20338
+ "mcp__marvin-governance__update_decision",
20339
+ "mcp__marvin-governance__update_action",
20340
+ "mcp__marvin-governance__update_question",
20341
+ "mcp__marvin-governance__list_decisions",
20342
+ "mcp__marvin-governance__list_actions",
20343
+ "mcp__marvin-governance__list_questions",
20344
+ "mcp__marvin-governance__get_decision",
20345
+ "mcp__marvin-governance__get_action",
20346
+ "mcp__marvin-governance__get_question"
20347
+ ];
20348
+ const conversation = query6({
20349
+ prompt: userPrompt,
20350
+ options: {
20351
+ systemPrompt,
20352
+ mcpServers: { "marvin-governance": mcpServer },
20353
+ permissionMode: "acceptEdits",
20354
+ maxTurns: 10,
20355
+ tools: [],
20356
+ allowedTools
20357
+ }
20358
+ });
20359
+ for await (const message of conversation) {
20360
+ handleContributeMessage(message, spinner);
20361
+ }
20362
+ const effects = [...createdArtifacts, ...updatedArtifacts];
20363
+ if (!draft && effects.length > 0) {
20364
+ appendEffectsToContribution(store, contributionId, contributionDoc.content, createdArtifacts, updatedArtifacts, store);
20365
+ }
20366
+ spinner.stop();
20367
+ if (draft) {
20368
+ console.log(chalk14.dim(`
20369
+ Draft proposal complete. No artifacts were created or updated.`));
20370
+ console.log(chalk14.dim(`Use --no-draft to execute effects directly.`));
20371
+ } else {
20372
+ const totalEffects = createdArtifacts.length + updatedArtifacts.length;
20373
+ console.log(chalk14.green(`
20374
+ Contribution ${contributionId} processed: ${totalEffects} effect${totalEffects === 1 ? "" : "s"}`));
20375
+ if (createdArtifacts.length > 0) {
20376
+ console.log(chalk14.dim(` Created: ${createdArtifacts.join(", ")}`));
20377
+ }
20378
+ if (updatedArtifacts.length > 0) {
20379
+ console.log(chalk14.dim(` Updated: ${updatedArtifacts.join(", ")}`));
20380
+ }
20381
+ }
20382
+ return { contributionId, effects, draft };
20383
+ } catch (err) {
20384
+ spinner.stop();
20385
+ throw err;
20386
+ }
20387
+ }
20388
+ function appendEffectsToContribution(store, contributionId, existingContent, created, updated, storeInstance) {
20389
+ const lines = [];
20390
+ if (created.length > 0) {
20391
+ lines.push("### Created");
20392
+ for (const id of created) {
20393
+ const doc = storeInstance.get(id);
20394
+ const title = doc ? doc.frontmatter.title : id;
20395
+ lines.push(`- ${id}: ${title}`);
20396
+ }
20397
+ }
20398
+ if (updated.length > 0) {
20399
+ lines.push("### Updated");
20400
+ for (const id of updated) {
20401
+ const doc = storeInstance.get(id);
20402
+ const title = doc ? doc.frontmatter.title : id;
20403
+ lines.push(`- ${id}: ${title}`);
20404
+ }
20405
+ }
20406
+ const effectsSection = `
20407
+
20408
+ ## Effects
20409
+ ${lines.join("\n")}`;
20410
+ const updatedContent = existingContent + effectsSection;
20411
+ store.update(contributionId, { status: "processed" }, updatedContent);
20412
+ }
20413
+ function handleContributeMessage(message, spinner) {
20414
+ switch (message.type) {
20415
+ case "assistant": {
20416
+ spinner.stop();
20417
+ const textBlocks = message.message.content.filter(
20418
+ (b) => b.type === "text"
20419
+ );
20420
+ if (textBlocks.length > 0) {
20421
+ console.log(
20422
+ chalk14.cyan("\nMarvin: ") + textBlocks.map((b) => b.text).join("\n")
20423
+ );
20424
+ }
20425
+ break;
20426
+ }
20427
+ case "system": {
20428
+ if (message.subtype === "init") {
20429
+ spinner.start("Analyzing contribution...");
20430
+ }
20431
+ break;
20432
+ }
20433
+ case "result": {
20434
+ spinner.stop();
20435
+ if (message.subtype !== "success") {
20436
+ console.log(
20437
+ chalk14.red(`
20438
+ Contribution analysis ended with error: ${message.subtype}`)
20439
+ );
20440
+ }
20441
+ break;
20442
+ }
20443
+ }
20444
+ }
20445
+
20446
+ // src/cli/commands/contribute.ts
20447
+ async function contributeCommand(options) {
20448
+ const project = loadProject();
20449
+ const marvinDir = project.marvinDir;
20450
+ if (!options.as) {
20451
+ console.log(chalk15.red("Missing required option: --as <persona>"));
20452
+ console.log(chalk15.dim('Example: marvin contribute --as tl --type action-result --prompt "..."'));
20453
+ return;
20454
+ }
20455
+ if (!options.type) {
20456
+ console.log(chalk15.red("Missing required option: --type <contribution-type>"));
20457
+ const personaId2 = resolvePersonaId(options.as);
20458
+ const persona2 = getPersona(personaId2);
20459
+ if (persona2?.contributionTypes?.length) {
20460
+ console.log(chalk15.dim(`Available types for ${persona2.name}: ${persona2.contributionTypes.join(", ")}`));
20461
+ }
20462
+ return;
20463
+ }
20464
+ if (!options.prompt) {
20465
+ console.log(chalk15.red("Missing required option: --prompt <text>"));
20466
+ console.log(chalk15.dim("Provide the contribution content via --prompt."));
20467
+ return;
20468
+ }
20469
+ const personaId = resolvePersonaId(options.as);
20470
+ const persona = getPersona(personaId);
20471
+ if (!persona) {
20472
+ console.log(chalk15.red(`Unknown persona: ${options.as}`));
20473
+ return;
20474
+ }
20475
+ const isDraft = options.draft !== false;
20476
+ console.log(chalk15.bold(`
20477
+ Contribution: ${options.type}`));
20478
+ console.log(chalk15.dim(`Persona: ${persona.name}`));
20479
+ console.log(chalk15.dim(`Mode: ${isDraft ? "draft (propose only)" : "direct (execute effects)"}`));
20480
+ if (options.about) {
20481
+ console.log(chalk15.dim(`About: ${options.about}`));
20482
+ }
20483
+ console.log();
20484
+ await contributeFromPersona({
20485
+ marvinDir,
20486
+ persona: options.as,
20487
+ contributionType: options.type,
20488
+ prompt: options.prompt,
20489
+ aboutArtifact: options.about,
20490
+ draft: isDraft
20491
+ });
20492
+ }
20493
+
19684
20494
  // src/cli/program.ts
19685
20495
  function createProgram() {
19686
20496
  const program2 = new Command();
19687
20497
  program2.name("marvin").description(
19688
20498
  "AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
19689
- ).version("0.2.3");
20499
+ ).version("0.2.5");
19690
20500
  program2.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
19691
20501
  await initCommand();
19692
20502
  });
@@ -19729,6 +20539,9 @@ function createProgram() {
19729
20539
  program2.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) => {
19730
20540
  await analyzeCommand(meetingId, options);
19731
20541
  });
20542
+ program2.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) => {
20543
+ await contributeCommand(options);
20544
+ });
19732
20545
  program2.command("import <path>").description("Import documents or sources from external paths").option("--dry-run", "Preview without writing files").option(
19733
20546
  "--conflict <strategy>",
19734
20547
  "ID conflict strategy: renumber, skip, overwrite (default: renumber)"