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/README.md +32 -18
- package/dist/index.d.ts +1 -0
- package/dist/index.js +862 -49
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +445 -42
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +887 -74
- package/dist/marvin.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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,
|
|
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
|
|
15166
|
+
import { tool as tool7 } from "@anthropic-ai/claude-agent-sdk";
|
|
14769
15167
|
function createUseCaseTools(store) {
|
|
14770
15168
|
return [
|
|
14771
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
15290
|
+
import { tool as tool8 } from "@anthropic-ai/claude-agent-sdk";
|
|
14893
15291
|
function createTechAssessmentTools(store) {
|
|
14894
15292
|
return [
|
|
14895
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
15452
|
+
import { tool as tool9 } from "@anthropic-ai/claude-agent-sdk";
|
|
15055
15453
|
function createExtensionDesignTools(store) {
|
|
15056
15454
|
return [
|
|
15057
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
15609
|
+
import { tool as tool10 } from "@anthropic-ai/claude-agent-sdk";
|
|
15212
15610
|
function createAemReportTools(store) {
|
|
15213
15611
|
return [
|
|
15214
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
16453
|
+
list(query7) {
|
|
16050
16454
|
const results = [];
|
|
16051
|
-
const types =
|
|
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 (
|
|
16063
|
-
if (
|
|
16064
|
-
if (
|
|
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
|
|
16671
|
+
import { tool as tool12 } from "@anthropic-ai/claude-agent-sdk";
|
|
16268
16672
|
function createDecisionTools(store) {
|
|
16269
16673
|
return [
|
|
16270
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
16777
|
+
import { tool as tool13 } from "@anthropic-ai/claude-agent-sdk";
|
|
16374
16778
|
function createActionTools(store) {
|
|
16375
16779
|
return [
|
|
16376
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
16895
|
+
import { tool as tool14 } from "@anthropic-ai/claude-agent-sdk";
|
|
16492
16896
|
function createQuestionTools(store) {
|
|
16493
16897
|
return [
|
|
16494
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
17004
|
+
import { tool as tool15 } from "@anthropic-ai/claude-agent-sdk";
|
|
16601
17005
|
function createDocumentTools(store) {
|
|
16602
17006
|
return [
|
|
16603
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
17098
|
+
import { tool as tool16 } from "@anthropic-ai/claude-agent-sdk";
|
|
16695
17099
|
function createSourceTools(manifest) {
|
|
16696
17100
|
return [
|
|
16697
|
-
|
|
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
|
-
|
|
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
|
|
17169
|
+
import { tool as tool17 } from "@anthropic-ai/claude-agent-sdk";
|
|
16766
17170
|
function createSessionTools(store) {
|
|
16767
17171
|
return [
|
|
16768
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
18943
|
+
import { tool as tool19 } from "@anthropic-ai/claude-agent-sdk";
|
|
18540
18944
|
function createPersonaTools(ctx, marvinDir) {
|
|
18541
18945
|
return [
|
|
18542
|
-
|
|
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
|
-
|
|
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.
|
|
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)"
|