mrvn-cli 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +16 -2
- package/dist/index.js +348 -125
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +87 -3
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +347 -127
- package/dist/marvin.js.map +1 -1
- package/dist/skills/builtin/governance-review/SKILL.md +11 -0
- package/dist/skills/builtin/governance-review/actions.yaml +21 -0
- package/dist/skills/builtin/governance-review/personas/delivery-manager.md +1 -0
- package/dist/skills/builtin/governance-review/personas/product-owner.md +1 -0
- package/package.json +1 -1
package/dist/marvin.js
CHANGED
|
@@ -6,7 +6,6 @@ var __export = (target, all) => {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// src/cli/program.ts
|
|
9
|
-
import { createRequire } from "module";
|
|
10
9
|
import { Command } from "commander";
|
|
11
10
|
|
|
12
11
|
// src/cli/commands/init.ts
|
|
@@ -1722,10 +1721,10 @@ var nanoid = /^[a-zA-Z0-9_-]{21}$/;
|
|
|
1722
1721
|
var duration = /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/;
|
|
1723
1722
|
var extendedDuration = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/;
|
|
1724
1723
|
var guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;
|
|
1725
|
-
var uuid = (
|
|
1726
|
-
if (!
|
|
1724
|
+
var uuid = (version2) => {
|
|
1725
|
+
if (!version2)
|
|
1727
1726
|
return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;
|
|
1728
|
-
return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${
|
|
1727
|
+
return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version2}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
|
|
1729
1728
|
};
|
|
1730
1729
|
var uuid4 = /* @__PURE__ */ uuid(4);
|
|
1731
1730
|
var uuid6 = /* @__PURE__ */ uuid(6);
|
|
@@ -13881,10 +13880,10 @@ function fromJSONSchema(schema, params) {
|
|
|
13881
13880
|
if (typeof schema === "boolean") {
|
|
13882
13881
|
return schema ? z.any() : z.never();
|
|
13883
13882
|
}
|
|
13884
|
-
const
|
|
13883
|
+
const version2 = detectVersion(schema, params?.defaultTarget);
|
|
13885
13884
|
const defs = schema.$defs || schema.definitions || {};
|
|
13886
13885
|
const ctx = {
|
|
13887
|
-
version:
|
|
13886
|
+
version: version2,
|
|
13888
13887
|
defs,
|
|
13889
13888
|
refs: /* @__PURE__ */ new Map(),
|
|
13890
13889
|
processing: /* @__PURE__ */ new Set(),
|
|
@@ -15928,7 +15927,7 @@ import * as readline from "readline";
|
|
|
15928
15927
|
import chalk2 from "chalk";
|
|
15929
15928
|
import ora from "ora";
|
|
15930
15929
|
import {
|
|
15931
|
-
query as
|
|
15930
|
+
query as query2
|
|
15932
15931
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
15933
15932
|
|
|
15934
15933
|
// src/personas/prompt-builder.ts
|
|
@@ -16977,7 +16976,9 @@ var SourceManifestManager = class {
|
|
|
16977
16976
|
// src/skills/registry.ts
|
|
16978
16977
|
import * as fs8 from "fs";
|
|
16979
16978
|
import * as path8 from "path";
|
|
16979
|
+
import { fileURLToPath } from "url";
|
|
16980
16980
|
import * as YAML6 from "yaml";
|
|
16981
|
+
import matter2 from "gray-matter";
|
|
16981
16982
|
|
|
16982
16983
|
// src/skills/builtin/governance-review.ts
|
|
16983
16984
|
var governanceReviewSkill = {
|
|
@@ -16985,6 +16986,7 @@ var governanceReviewSkill = {
|
|
|
16985
16986
|
name: "Governance Review",
|
|
16986
16987
|
description: "Review open governance items and generate summaries",
|
|
16987
16988
|
version: "1.0.0",
|
|
16989
|
+
format: "builtin-ts",
|
|
16988
16990
|
personas: ["delivery-manager", "product-owner"],
|
|
16989
16991
|
promptFragments: {
|
|
16990
16992
|
"delivery-manager": `You have the **Governance Review** skill. You can proactively review all open governance items (decisions, actions, questions) and produce structured summaries with recommendations. Use the \`governance-review__summarize\` tool to run a comprehensive review.`,
|
|
@@ -17019,11 +17021,98 @@ Be thorough but concise. Focus on actionable insights.`,
|
|
|
17019
17021
|
var BUILTIN_SKILLS = {
|
|
17020
17022
|
"governance-review": governanceReviewSkill
|
|
17021
17023
|
};
|
|
17024
|
+
var GOVERNANCE_TOOL_NAMES = [
|
|
17025
|
+
"mcp__marvin-governance__list_decisions",
|
|
17026
|
+
"mcp__marvin-governance__get_decision",
|
|
17027
|
+
"mcp__marvin-governance__create_decision",
|
|
17028
|
+
"mcp__marvin-governance__update_decision",
|
|
17029
|
+
"mcp__marvin-governance__list_actions",
|
|
17030
|
+
"mcp__marvin-governance__get_action",
|
|
17031
|
+
"mcp__marvin-governance__create_action",
|
|
17032
|
+
"mcp__marvin-governance__update_action",
|
|
17033
|
+
"mcp__marvin-governance__list_questions",
|
|
17034
|
+
"mcp__marvin-governance__get_question",
|
|
17035
|
+
"mcp__marvin-governance__create_question",
|
|
17036
|
+
"mcp__marvin-governance__update_question",
|
|
17037
|
+
"mcp__marvin-governance__search_documents",
|
|
17038
|
+
"mcp__marvin-governance__read_document",
|
|
17039
|
+
"mcp__marvin-governance__project_summary"
|
|
17040
|
+
];
|
|
17041
|
+
function getBuiltinSkillsDir() {
|
|
17042
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
17043
|
+
return path8.join(path8.dirname(thisFile), "builtin");
|
|
17044
|
+
}
|
|
17045
|
+
function loadSkillFromDirectory(dirPath) {
|
|
17046
|
+
const skillMdPath = path8.join(dirPath, "SKILL.md");
|
|
17047
|
+
if (!fs8.existsSync(skillMdPath)) return void 0;
|
|
17048
|
+
try {
|
|
17049
|
+
const raw = fs8.readFileSync(skillMdPath, "utf-8");
|
|
17050
|
+
const { data, content } = matter2(raw);
|
|
17051
|
+
if (!data.name || !data.description) return void 0;
|
|
17052
|
+
const metadata = data.metadata ?? {};
|
|
17053
|
+
const version2 = metadata.version ?? "1.0.0";
|
|
17054
|
+
const personas = metadata.personas;
|
|
17055
|
+
const promptFragments = {};
|
|
17056
|
+
const wildcardPrompt = content.trim();
|
|
17057
|
+
if (wildcardPrompt) {
|
|
17058
|
+
promptFragments["*"] = wildcardPrompt;
|
|
17059
|
+
}
|
|
17060
|
+
const personasDir = path8.join(dirPath, "personas");
|
|
17061
|
+
if (fs8.existsSync(personasDir)) {
|
|
17062
|
+
try {
|
|
17063
|
+
for (const file2 of fs8.readdirSync(personasDir)) {
|
|
17064
|
+
if (!file2.endsWith(".md")) continue;
|
|
17065
|
+
const personaId = file2.replace(/\.md$/, "");
|
|
17066
|
+
const personaPrompt = fs8.readFileSync(path8.join(personasDir, file2), "utf-8").trim();
|
|
17067
|
+
if (personaPrompt) {
|
|
17068
|
+
promptFragments[personaId] = personaPrompt;
|
|
17069
|
+
}
|
|
17070
|
+
}
|
|
17071
|
+
} catch {
|
|
17072
|
+
}
|
|
17073
|
+
}
|
|
17074
|
+
let actions;
|
|
17075
|
+
const actionsPath = path8.join(dirPath, "actions.yaml");
|
|
17076
|
+
if (fs8.existsSync(actionsPath)) {
|
|
17077
|
+
try {
|
|
17078
|
+
const actionsRaw = fs8.readFileSync(actionsPath, "utf-8");
|
|
17079
|
+
actions = YAML6.parse(actionsRaw);
|
|
17080
|
+
} catch {
|
|
17081
|
+
}
|
|
17082
|
+
}
|
|
17083
|
+
return {
|
|
17084
|
+
id: data.name,
|
|
17085
|
+
name: data.name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
17086
|
+
description: data.description,
|
|
17087
|
+
version: version2,
|
|
17088
|
+
format: "skill-md",
|
|
17089
|
+
dirPath,
|
|
17090
|
+
personas,
|
|
17091
|
+
promptFragments: Object.keys(promptFragments).length > 0 ? promptFragments : void 0,
|
|
17092
|
+
actions
|
|
17093
|
+
};
|
|
17094
|
+
} catch {
|
|
17095
|
+
return void 0;
|
|
17096
|
+
}
|
|
17097
|
+
}
|
|
17022
17098
|
function loadAllSkills(marvinDir) {
|
|
17023
17099
|
const skills = /* @__PURE__ */ new Map();
|
|
17024
17100
|
for (const [id, skill] of Object.entries(BUILTIN_SKILLS)) {
|
|
17025
17101
|
skills.set(id, skill);
|
|
17026
17102
|
}
|
|
17103
|
+
try {
|
|
17104
|
+
const builtinDir = getBuiltinSkillsDir();
|
|
17105
|
+
if (fs8.existsSync(builtinDir)) {
|
|
17106
|
+
for (const entry of fs8.readdirSync(builtinDir)) {
|
|
17107
|
+
const entryPath = path8.join(builtinDir, entry);
|
|
17108
|
+
if (!fs8.statSync(entryPath).isDirectory()) continue;
|
|
17109
|
+
if (skills.has(entry)) continue;
|
|
17110
|
+
const skill = loadSkillFromDirectory(entryPath);
|
|
17111
|
+
if (skill) skills.set(skill.id, skill);
|
|
17112
|
+
}
|
|
17113
|
+
}
|
|
17114
|
+
} catch {
|
|
17115
|
+
}
|
|
17027
17116
|
if (marvinDir) {
|
|
17028
17117
|
const skillsDir = path8.join(marvinDir, "skills");
|
|
17029
17118
|
if (fs8.existsSync(skillsDir)) {
|
|
@@ -17033,10 +17122,20 @@ function loadAllSkills(marvinDir) {
|
|
|
17033
17122
|
} catch {
|
|
17034
17123
|
entries = [];
|
|
17035
17124
|
}
|
|
17036
|
-
for (const
|
|
17037
|
-
|
|
17125
|
+
for (const entry of entries) {
|
|
17126
|
+
const entryPath = path8.join(skillsDir, entry);
|
|
17127
|
+
try {
|
|
17128
|
+
if (fs8.statSync(entryPath).isDirectory()) {
|
|
17129
|
+
const skill = loadSkillFromDirectory(entryPath);
|
|
17130
|
+
if (skill) skills.set(skill.id, skill);
|
|
17131
|
+
continue;
|
|
17132
|
+
}
|
|
17133
|
+
} catch {
|
|
17134
|
+
continue;
|
|
17135
|
+
}
|
|
17136
|
+
if (!entry.endsWith(".yaml") && !entry.endsWith(".yml")) continue;
|
|
17038
17137
|
try {
|
|
17039
|
-
const raw = fs8.readFileSync(
|
|
17138
|
+
const raw = fs8.readFileSync(entryPath, "utf-8");
|
|
17040
17139
|
const parsed = YAML6.parse(raw);
|
|
17041
17140
|
if (!parsed?.id || !parsed?.name || !parsed?.version) continue;
|
|
17042
17141
|
const skill = {
|
|
@@ -17044,6 +17143,7 @@ function loadAllSkills(marvinDir) {
|
|
|
17044
17143
|
name: parsed.name,
|
|
17045
17144
|
description: parsed.description ?? "",
|
|
17046
17145
|
version: parsed.version,
|
|
17146
|
+
format: "yaml",
|
|
17047
17147
|
personas: parsed.personas,
|
|
17048
17148
|
promptFragments: parsed.promptFragments,
|
|
17049
17149
|
actions: parsed.actions
|
|
@@ -17106,91 +17206,73 @@ function listAllSkillInfo(allSkills, skillsConfig, personaIds) {
|
|
|
17106
17206
|
name: skill.name,
|
|
17107
17207
|
version: skill.version,
|
|
17108
17208
|
description: skill.description,
|
|
17209
|
+
format: skill.format,
|
|
17109
17210
|
assignedPersonas
|
|
17110
17211
|
});
|
|
17111
17212
|
}
|
|
17112
17213
|
return result;
|
|
17113
17214
|
}
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
|
|
17117
|
-
|
|
17118
|
-
|
|
17119
|
-
|
|
17120
|
-
|
|
17121
|
-
|
|
17122
|
-
|
|
17123
|
-
|
|
17124
|
-
"mcp__marvin-governance__update_decision",
|
|
17125
|
-
"mcp__marvin-governance__list_actions",
|
|
17126
|
-
"mcp__marvin-governance__get_action",
|
|
17127
|
-
"mcp__marvin-governance__create_action",
|
|
17128
|
-
"mcp__marvin-governance__update_action",
|
|
17129
|
-
"mcp__marvin-governance__list_questions",
|
|
17130
|
-
"mcp__marvin-governance__get_question",
|
|
17131
|
-
"mcp__marvin-governance__create_question",
|
|
17132
|
-
"mcp__marvin-governance__update_question",
|
|
17133
|
-
"mcp__marvin-governance__search_documents",
|
|
17134
|
-
"mcp__marvin-governance__read_document",
|
|
17135
|
-
"mcp__marvin-governance__project_summary"
|
|
17136
|
-
];
|
|
17137
|
-
async function runSkillAction(action, userPrompt, context) {
|
|
17138
|
-
try {
|
|
17139
|
-
const mcpServer = createMarvinMcpServer(context.store);
|
|
17140
|
-
const allowedTools = action.allowGovernanceTools !== false ? GOVERNANCE_TOOL_NAMES : [];
|
|
17141
|
-
const conversation = query2({
|
|
17142
|
-
prompt: userPrompt,
|
|
17143
|
-
options: {
|
|
17144
|
-
systemPrompt: action.systemPrompt,
|
|
17145
|
-
mcpServers: { "marvin-governance": mcpServer },
|
|
17215
|
+
function getSkillAgentDefinitions(skillIds, allSkills) {
|
|
17216
|
+
const agents = {};
|
|
17217
|
+
for (const id of skillIds) {
|
|
17218
|
+
const skill = allSkills.get(id);
|
|
17219
|
+
if (!skill?.actions) continue;
|
|
17220
|
+
for (const action of skill.actions) {
|
|
17221
|
+
const agentKey = `${skill.id}__${action.id}`;
|
|
17222
|
+
agents[agentKey] = {
|
|
17223
|
+
description: action.description,
|
|
17224
|
+
prompt: action.systemPrompt,
|
|
17146
17225
|
maxTurns: action.maxTurns ?? 5,
|
|
17147
|
-
|
|
17148
|
-
|
|
17149
|
-
|
|
17150
|
-
|
|
17151
|
-
|
|
17152
|
-
|
|
17153
|
-
|
|
17154
|
-
|
|
17155
|
-
|
|
17156
|
-
|
|
17157
|
-
|
|
17226
|
+
tools: action.allowGovernanceTools !== false ? GOVERNANCE_TOOL_NAMES : []
|
|
17227
|
+
};
|
|
17228
|
+
}
|
|
17229
|
+
}
|
|
17230
|
+
return agents;
|
|
17231
|
+
}
|
|
17232
|
+
function migrateYamlToSkillMd(yamlPath, outputDir) {
|
|
17233
|
+
const raw = fs8.readFileSync(yamlPath, "utf-8");
|
|
17234
|
+
const parsed = YAML6.parse(raw);
|
|
17235
|
+
if (!parsed?.id || !parsed?.name) {
|
|
17236
|
+
throw new Error(`Invalid skill YAML: missing required fields (id, name)`);
|
|
17237
|
+
}
|
|
17238
|
+
fs8.mkdirSync(outputDir, { recursive: true });
|
|
17239
|
+
const frontmatter = {
|
|
17240
|
+
name: parsed.id,
|
|
17241
|
+
description: parsed.description ?? ""
|
|
17242
|
+
};
|
|
17243
|
+
const metadata = {};
|
|
17244
|
+
if (parsed.version) metadata.version = parsed.version;
|
|
17245
|
+
if (parsed.personas) metadata.personas = parsed.personas;
|
|
17246
|
+
if (Object.keys(metadata).length > 0) frontmatter.metadata = metadata;
|
|
17247
|
+
const promptFragments = parsed.promptFragments;
|
|
17248
|
+
const wildcardPrompt = promptFragments?.["*"] ?? "";
|
|
17249
|
+
const skillMd = matter2.stringify(wildcardPrompt ? `
|
|
17250
|
+
${wildcardPrompt}
|
|
17251
|
+
` : "\n", frontmatter);
|
|
17252
|
+
fs8.writeFileSync(path8.join(outputDir, "SKILL.md"), skillMd, "utf-8");
|
|
17253
|
+
if (promptFragments) {
|
|
17254
|
+
const personaKeys = Object.keys(promptFragments).filter((k) => k !== "*");
|
|
17255
|
+
if (personaKeys.length > 0) {
|
|
17256
|
+
const personasDir = path8.join(outputDir, "personas");
|
|
17257
|
+
fs8.mkdirSync(personasDir, { recursive: true });
|
|
17258
|
+
for (const personaId of personaKeys) {
|
|
17259
|
+
fs8.writeFileSync(
|
|
17260
|
+
path8.join(personasDir, `${personaId}.md`),
|
|
17261
|
+
`${promptFragments[personaId]}
|
|
17262
|
+
`,
|
|
17263
|
+
"utf-8"
|
|
17158
17264
|
);
|
|
17159
|
-
for (const block of textBlocks) {
|
|
17160
|
-
textParts.push(block.text);
|
|
17161
|
-
}
|
|
17162
17265
|
}
|
|
17163
17266
|
}
|
|
17164
|
-
return {
|
|
17165
|
-
content: [{ type: "text", text: textParts.join("\n") || "Action completed with no output." }]
|
|
17166
|
-
};
|
|
17167
|
-
} catch (err) {
|
|
17168
|
-
return {
|
|
17169
|
-
content: [{ type: "text", text: `Skill action failed: ${err}` }],
|
|
17170
|
-
isError: true
|
|
17171
|
-
};
|
|
17172
17267
|
}
|
|
17173
|
-
|
|
17174
|
-
|
|
17175
|
-
|
|
17176
|
-
|
|
17177
|
-
|
|
17178
|
-
|
|
17179
|
-
|
|
17180
|
-
for (const action of skill.actions) {
|
|
17181
|
-
tools.push(
|
|
17182
|
-
tool16(
|
|
17183
|
-
`${skill.id}__${action.id}`,
|
|
17184
|
-
action.description,
|
|
17185
|
-
{
|
|
17186
|
-
prompt: external_exports.string().describe("What you want this action to do")
|
|
17187
|
-
},
|
|
17188
|
-
async (args) => runSkillAction(action, args.prompt, context)
|
|
17189
|
-
)
|
|
17190
|
-
);
|
|
17191
|
-
}
|
|
17268
|
+
const actions = parsed.actions;
|
|
17269
|
+
if (actions && actions.length > 0) {
|
|
17270
|
+
fs8.writeFileSync(
|
|
17271
|
+
path8.join(outputDir, "actions.yaml"),
|
|
17272
|
+
YAML6.stringify(actions),
|
|
17273
|
+
"utf-8"
|
|
17274
|
+
);
|
|
17192
17275
|
}
|
|
17193
|
-
return tools;
|
|
17194
17276
|
}
|
|
17195
17277
|
|
|
17196
17278
|
// src/agent/session.ts
|
|
@@ -17208,15 +17290,14 @@ async function startSession(options) {
|
|
|
17208
17290
|
const allSkills = loadAllSkills(marvinDir);
|
|
17209
17291
|
const skillIds = resolveSkillsForPersona(persona.id, config2.project.skills, allSkills);
|
|
17210
17292
|
const codeSkillTools = getSkillTools(skillIds, allSkills, store);
|
|
17211
|
-
const
|
|
17212
|
-
const actionTools = createSkillActionTools(skillsWithActions, { store, marvinDir, projectRoot });
|
|
17293
|
+
const skillAgents = getSkillAgentDefinitions(skillIds, allSkills);
|
|
17213
17294
|
const skillPromptFragment = getSkillPromptFragment(skillIds, allSkills, persona.id);
|
|
17214
17295
|
const mcpServer = createMarvinMcpServer(store, {
|
|
17215
17296
|
manifest,
|
|
17216
17297
|
sourcesDir: hasSourcesDir ? sourcesDir : void 0,
|
|
17217
17298
|
sessionStore,
|
|
17218
17299
|
pluginTools,
|
|
17219
|
-
skillTools:
|
|
17300
|
+
skillTools: codeSkillTools
|
|
17220
17301
|
});
|
|
17221
17302
|
const systemPrompt = buildSystemPrompt(persona, config2.project, pluginPromptFragment, skillPromptFragment);
|
|
17222
17303
|
let existingSession;
|
|
@@ -17306,14 +17387,16 @@ Marvin \u2014 ${persona.name}
|
|
|
17306
17387
|
"mcp__marvin-governance__get_session",
|
|
17307
17388
|
"mcp__marvin-governance__analyze_meeting",
|
|
17308
17389
|
...pluginTools.map((t) => `mcp__marvin-governance__${t.name}`),
|
|
17309
|
-
...codeSkillTools.map((t) => `mcp__marvin-governance__${t.name}`)
|
|
17310
|
-
...actionTools.map((t) => `mcp__marvin-governance__${t.name}`)
|
|
17390
|
+
...codeSkillTools.map((t) => `mcp__marvin-governance__${t.name}`)
|
|
17311
17391
|
]
|
|
17312
17392
|
};
|
|
17393
|
+
if (Object.keys(skillAgents).length > 0) {
|
|
17394
|
+
queryOptions.agents = skillAgents;
|
|
17395
|
+
}
|
|
17313
17396
|
if (existingSession) {
|
|
17314
17397
|
queryOptions.resume = existingSession.id;
|
|
17315
17398
|
}
|
|
17316
|
-
const conversation =
|
|
17399
|
+
const conversation = query2({
|
|
17317
17400
|
prompt,
|
|
17318
17401
|
options: queryOptions
|
|
17319
17402
|
});
|
|
@@ -17635,7 +17718,7 @@ import * as fs10 from "fs";
|
|
|
17635
17718
|
import * as path10 from "path";
|
|
17636
17719
|
import chalk7 from "chalk";
|
|
17637
17720
|
import ora2 from "ora";
|
|
17638
|
-
import { query as
|
|
17721
|
+
import { query as query3 } from "@anthropic-ai/claude-agent-sdk";
|
|
17639
17722
|
|
|
17640
17723
|
// src/sources/prompts.ts
|
|
17641
17724
|
function buildIngestSystemPrompt(persona, projectConfig, isDraft) {
|
|
@@ -17768,7 +17851,7 @@ async function ingestFile(options) {
|
|
|
17768
17851
|
const spinner = ora2({ text: `Analyzing ${fileName}...`, color: "cyan" });
|
|
17769
17852
|
spinner.start();
|
|
17770
17853
|
try {
|
|
17771
|
-
const conversation =
|
|
17854
|
+
const conversation = query3({
|
|
17772
17855
|
prompt: userPrompt,
|
|
17773
17856
|
options: {
|
|
17774
17857
|
systemPrompt,
|
|
@@ -17987,8 +18070,8 @@ var MarvinGit = class {
|
|
|
17987
18070
|
);
|
|
17988
18071
|
}
|
|
17989
18072
|
await this.git.init();
|
|
17990
|
-
const { writeFileSync:
|
|
17991
|
-
|
|
18073
|
+
const { writeFileSync: writeFileSync9 } = await import("fs");
|
|
18074
|
+
writeFileSync9(
|
|
17992
18075
|
path12.join(this.marvinDir, ".gitignore"),
|
|
17993
18076
|
MARVIN_GITIGNORE,
|
|
17994
18077
|
"utf-8"
|
|
@@ -18293,6 +18376,89 @@ import * as fs12 from "fs";
|
|
|
18293
18376
|
import * as path13 from "path";
|
|
18294
18377
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
18295
18378
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
18379
|
+
|
|
18380
|
+
// src/skills/action-tools.ts
|
|
18381
|
+
import { tool as tool16 } from "@anthropic-ai/claude-agent-sdk";
|
|
18382
|
+
|
|
18383
|
+
// src/skills/action-runner.ts
|
|
18384
|
+
import { query as query4 } from "@anthropic-ai/claude-agent-sdk";
|
|
18385
|
+
var GOVERNANCE_TOOL_NAMES2 = [
|
|
18386
|
+
"mcp__marvin-governance__list_decisions",
|
|
18387
|
+
"mcp__marvin-governance__get_decision",
|
|
18388
|
+
"mcp__marvin-governance__create_decision",
|
|
18389
|
+
"mcp__marvin-governance__update_decision",
|
|
18390
|
+
"mcp__marvin-governance__list_actions",
|
|
18391
|
+
"mcp__marvin-governance__get_action",
|
|
18392
|
+
"mcp__marvin-governance__create_action",
|
|
18393
|
+
"mcp__marvin-governance__update_action",
|
|
18394
|
+
"mcp__marvin-governance__list_questions",
|
|
18395
|
+
"mcp__marvin-governance__get_question",
|
|
18396
|
+
"mcp__marvin-governance__create_question",
|
|
18397
|
+
"mcp__marvin-governance__update_question",
|
|
18398
|
+
"mcp__marvin-governance__search_documents",
|
|
18399
|
+
"mcp__marvin-governance__read_document",
|
|
18400
|
+
"mcp__marvin-governance__project_summary"
|
|
18401
|
+
];
|
|
18402
|
+
async function runSkillAction(action, userPrompt, context) {
|
|
18403
|
+
try {
|
|
18404
|
+
const mcpServer = createMarvinMcpServer(context.store);
|
|
18405
|
+
const allowedTools = action.allowGovernanceTools !== false ? GOVERNANCE_TOOL_NAMES2 : [];
|
|
18406
|
+
const conversation = query4({
|
|
18407
|
+
prompt: userPrompt,
|
|
18408
|
+
options: {
|
|
18409
|
+
systemPrompt: action.systemPrompt,
|
|
18410
|
+
mcpServers: { "marvin-governance": mcpServer },
|
|
18411
|
+
maxTurns: action.maxTurns ?? 5,
|
|
18412
|
+
allowedTools,
|
|
18413
|
+
cwd: context.projectRoot,
|
|
18414
|
+
tools: [],
|
|
18415
|
+
permissionMode: "acceptEdits"
|
|
18416
|
+
}
|
|
18417
|
+
});
|
|
18418
|
+
const textParts = [];
|
|
18419
|
+
for await (const message of conversation) {
|
|
18420
|
+
if (message.type === "assistant") {
|
|
18421
|
+
const textBlocks = message.message.content.filter(
|
|
18422
|
+
(b) => b.type === "text"
|
|
18423
|
+
);
|
|
18424
|
+
for (const block of textBlocks) {
|
|
18425
|
+
textParts.push(block.text);
|
|
18426
|
+
}
|
|
18427
|
+
}
|
|
18428
|
+
}
|
|
18429
|
+
return {
|
|
18430
|
+
content: [{ type: "text", text: textParts.join("\n") || "Action completed with no output." }]
|
|
18431
|
+
};
|
|
18432
|
+
} catch (err) {
|
|
18433
|
+
return {
|
|
18434
|
+
content: [{ type: "text", text: `Skill action failed: ${err}` }],
|
|
18435
|
+
isError: true
|
|
18436
|
+
};
|
|
18437
|
+
}
|
|
18438
|
+
}
|
|
18439
|
+
|
|
18440
|
+
// src/skills/action-tools.ts
|
|
18441
|
+
function createSkillActionTools(skills, context) {
|
|
18442
|
+
const tools = [];
|
|
18443
|
+
for (const skill of skills) {
|
|
18444
|
+
if (!skill.actions) continue;
|
|
18445
|
+
for (const action of skill.actions) {
|
|
18446
|
+
tools.push(
|
|
18447
|
+
tool16(
|
|
18448
|
+
`${skill.id}__${action.id}`,
|
|
18449
|
+
action.description,
|
|
18450
|
+
{
|
|
18451
|
+
prompt: external_exports.string().describe("What you want this action to do")
|
|
18452
|
+
},
|
|
18453
|
+
async (args) => runSkillAction(action, args.prompt, context)
|
|
18454
|
+
)
|
|
18455
|
+
);
|
|
18456
|
+
}
|
|
18457
|
+
}
|
|
18458
|
+
return tools;
|
|
18459
|
+
}
|
|
18460
|
+
|
|
18461
|
+
// src/mcp/stdio-server.ts
|
|
18296
18462
|
function collectTools(marvinDir) {
|
|
18297
18463
|
const config2 = loadProjectConfig(marvinDir);
|
|
18298
18464
|
const plugin = resolvePlugin(config2.methodology);
|
|
@@ -18370,6 +18536,7 @@ async function serveCommand() {
|
|
|
18370
18536
|
import * as fs13 from "fs";
|
|
18371
18537
|
import * as path14 from "path";
|
|
18372
18538
|
import * as YAML7 from "yaml";
|
|
18539
|
+
import matter3 from "gray-matter";
|
|
18373
18540
|
import chalk10 from "chalk";
|
|
18374
18541
|
async function skillsListCommand() {
|
|
18375
18542
|
const project = loadProject();
|
|
@@ -18383,10 +18550,12 @@ async function skillsListCommand() {
|
|
|
18383
18550
|
}
|
|
18384
18551
|
console.log(chalk10.bold("\nAvailable Skills\n"));
|
|
18385
18552
|
const idWidth = Math.max(5, ...infos.map((s) => s.id.length));
|
|
18553
|
+
const fmtWidth = Math.max(6, ...infos.map((s) => s.format.length));
|
|
18386
18554
|
const verWidth = Math.max(7, ...infos.map((s) => s.version.length));
|
|
18387
18555
|
const descWidth = Math.max(11, ...infos.map((s) => s.description.length));
|
|
18388
18556
|
const header = [
|
|
18389
18557
|
"ID".padEnd(idWidth),
|
|
18558
|
+
"Format".padEnd(fmtWidth),
|
|
18390
18559
|
"Version".padEnd(verWidth),
|
|
18391
18560
|
"Description".padEnd(descWidth),
|
|
18392
18561
|
"Personas"
|
|
@@ -18398,6 +18567,7 @@ async function skillsListCommand() {
|
|
|
18398
18567
|
console.log(
|
|
18399
18568
|
[
|
|
18400
18569
|
info.id.padEnd(idWidth),
|
|
18570
|
+
info.format.padEnd(fmtWidth),
|
|
18401
18571
|
info.version.padEnd(verWidth),
|
|
18402
18572
|
info.description.padEnd(descWidth),
|
|
18403
18573
|
personas
|
|
@@ -18466,33 +18636,82 @@ async function skillsCreateCommand(name) {
|
|
|
18466
18636
|
const project = loadProject();
|
|
18467
18637
|
const skillsDir = path14.join(project.marvinDir, "skills");
|
|
18468
18638
|
fs13.mkdirSync(skillsDir, { recursive: true });
|
|
18469
|
-
const
|
|
18470
|
-
if (fs13.existsSync(
|
|
18471
|
-
console.log(chalk10.yellow(`Skill
|
|
18639
|
+
const skillDir = path14.join(skillsDir, name);
|
|
18640
|
+
if (fs13.existsSync(skillDir)) {
|
|
18641
|
+
console.log(chalk10.yellow(`Skill directory already exists: ${skillDir}`));
|
|
18472
18642
|
return;
|
|
18473
18643
|
}
|
|
18474
|
-
|
|
18475
|
-
|
|
18476
|
-
|
|
18644
|
+
fs13.mkdirSync(skillDir, { recursive: true });
|
|
18645
|
+
const displayName = name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
18646
|
+
const frontmatter = {
|
|
18647
|
+
name,
|
|
18477
18648
|
description: `Custom skill: ${name}`,
|
|
18478
|
-
|
|
18479
|
-
|
|
18480
|
-
|
|
18481
|
-
|
|
18482
|
-
},
|
|
18483
|
-
actions: [
|
|
18484
|
-
{
|
|
18485
|
-
id: "run",
|
|
18486
|
-
name: `Run ${name}`,
|
|
18487
|
-
description: `Execute the ${name} skill`,
|
|
18488
|
-
systemPrompt: "You are a helpful assistant. Complete the requested task using the available governance tools.",
|
|
18489
|
-
maxTurns: 5
|
|
18490
|
-
}
|
|
18491
|
-
]
|
|
18649
|
+
metadata: {
|
|
18650
|
+
version: "1.0.0",
|
|
18651
|
+
personas: ["product-owner"]
|
|
18652
|
+
}
|
|
18492
18653
|
};
|
|
18493
|
-
|
|
18494
|
-
|
|
18495
|
-
|
|
18654
|
+
const body = `
|
|
18655
|
+
You have the **${displayName}** skill.
|
|
18656
|
+
`;
|
|
18657
|
+
const skillMd = matter3.stringify(body, frontmatter);
|
|
18658
|
+
fs13.writeFileSync(path14.join(skillDir, "SKILL.md"), skillMd, "utf-8");
|
|
18659
|
+
const actions = [
|
|
18660
|
+
{
|
|
18661
|
+
id: "run",
|
|
18662
|
+
name: `Run ${name}`,
|
|
18663
|
+
description: `Execute the ${name} skill`,
|
|
18664
|
+
systemPrompt: "You are a helpful assistant. Complete the requested task using the available governance tools.",
|
|
18665
|
+
maxTurns: 5
|
|
18666
|
+
}
|
|
18667
|
+
];
|
|
18668
|
+
fs13.writeFileSync(path14.join(skillDir, "actions.yaml"), YAML7.stringify(actions), "utf-8");
|
|
18669
|
+
console.log(chalk10.green(`Created skill: ${skillDir}/`));
|
|
18670
|
+
console.log(chalk10.dim(" SKILL.md \u2014 skill definition and prompt"));
|
|
18671
|
+
console.log(chalk10.dim(" actions.yaml \u2014 action definitions"));
|
|
18672
|
+
console.log(chalk10.dim("\nAdd persona-specific prompts in personas/<persona-id>.md"));
|
|
18673
|
+
}
|
|
18674
|
+
async function skillsMigrateCommand() {
|
|
18675
|
+
const project = loadProject();
|
|
18676
|
+
const skillsDir = path14.join(project.marvinDir, "skills");
|
|
18677
|
+
if (!fs13.existsSync(skillsDir)) {
|
|
18678
|
+
console.log(chalk10.dim("No skills directory found."));
|
|
18679
|
+
return;
|
|
18680
|
+
}
|
|
18681
|
+
let entries;
|
|
18682
|
+
try {
|
|
18683
|
+
entries = fs13.readdirSync(skillsDir);
|
|
18684
|
+
} catch {
|
|
18685
|
+
console.log(chalk10.red("Could not read skills directory."));
|
|
18686
|
+
return;
|
|
18687
|
+
}
|
|
18688
|
+
const yamlFiles = entries.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
18689
|
+
if (yamlFiles.length === 0) {
|
|
18690
|
+
console.log(chalk10.dim("No YAML skill files to migrate."));
|
|
18691
|
+
return;
|
|
18692
|
+
}
|
|
18693
|
+
let migrated = 0;
|
|
18694
|
+
for (const file2 of yamlFiles) {
|
|
18695
|
+
const yamlPath = path14.join(skillsDir, file2);
|
|
18696
|
+
const baseName = file2.replace(/\.(yaml|yml)$/, "");
|
|
18697
|
+
const outputDir = path14.join(skillsDir, baseName);
|
|
18698
|
+
if (fs13.existsSync(outputDir)) {
|
|
18699
|
+
console.log(chalk10.yellow(`Skipping "${file2}" \u2014 directory "${baseName}/" already exists.`));
|
|
18700
|
+
continue;
|
|
18701
|
+
}
|
|
18702
|
+
try {
|
|
18703
|
+
migrateYamlToSkillMd(yamlPath, outputDir);
|
|
18704
|
+
fs13.renameSync(yamlPath, `${yamlPath}.bak`);
|
|
18705
|
+
console.log(chalk10.green(`Migrated "${file2}" \u2192 "${baseName}/"`));
|
|
18706
|
+
migrated++;
|
|
18707
|
+
} catch (err) {
|
|
18708
|
+
console.log(chalk10.red(`Failed to migrate "${file2}": ${err}`));
|
|
18709
|
+
}
|
|
18710
|
+
}
|
|
18711
|
+
if (migrated > 0) {
|
|
18712
|
+
console.log(chalk10.dim(`
|
|
18713
|
+
${migrated} skill(s) migrated. Original files renamed to *.bak`));
|
|
18714
|
+
}
|
|
18496
18715
|
}
|
|
18497
18716
|
|
|
18498
18717
|
// src/cli/commands/import.ts
|
|
@@ -18503,12 +18722,12 @@ import chalk11 from "chalk";
|
|
|
18503
18722
|
// src/import/engine.ts
|
|
18504
18723
|
import * as fs15 from "fs";
|
|
18505
18724
|
import * as path16 from "path";
|
|
18506
|
-
import
|
|
18725
|
+
import matter5 from "gray-matter";
|
|
18507
18726
|
|
|
18508
18727
|
// src/import/classifier.ts
|
|
18509
18728
|
import * as fs14 from "fs";
|
|
18510
18729
|
import * as path15 from "path";
|
|
18511
|
-
import
|
|
18730
|
+
import matter4 from "gray-matter";
|
|
18512
18731
|
var RAW_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".pdf", ".txt"]);
|
|
18513
18732
|
var CORE_DIR_NAMES = /* @__PURE__ */ new Set(["decisions", "actions", "questions"]);
|
|
18514
18733
|
var ID_PATTERN = /^[A-Z]+-\d{3,}$/;
|
|
@@ -18534,7 +18753,7 @@ function classifyPath(inputPath, knownTypes, knownDirNames) {
|
|
|
18534
18753
|
const hasMarvinDocs = mdFiles.some((f) => {
|
|
18535
18754
|
try {
|
|
18536
18755
|
const raw = fs14.readFileSync(path15.join(resolved, f), "utf-8");
|
|
18537
|
-
const { data } =
|
|
18756
|
+
const { data } = matter4(raw);
|
|
18538
18757
|
return isValidMarvinDocument(data, knownTypes);
|
|
18539
18758
|
} catch {
|
|
18540
18759
|
return false;
|
|
@@ -18555,7 +18774,7 @@ function classifyFile(filePath, knownTypes) {
|
|
|
18555
18774
|
if (ext === ".md") {
|
|
18556
18775
|
try {
|
|
18557
18776
|
const raw = fs14.readFileSync(resolved, "utf-8");
|
|
18558
|
-
const { data } =
|
|
18777
|
+
const { data } = matter4(raw);
|
|
18559
18778
|
if (isValidMarvinDocument(data, knownTypes)) {
|
|
18560
18779
|
return { type: "marvin-document", inputPath: resolved };
|
|
18561
18780
|
}
|
|
@@ -18766,7 +18985,7 @@ function collectMarvinDocs(dir, knownTypes) {
|
|
|
18766
18985
|
const filePath = path16.join(dir, file2);
|
|
18767
18986
|
try {
|
|
18768
18987
|
const raw = fs15.readFileSync(filePath, "utf-8");
|
|
18769
|
-
const { data, content } =
|
|
18988
|
+
const { data, content } = matter5(raw);
|
|
18770
18989
|
if (isValidMarvinDocument(data, knownTypes)) {
|
|
18771
18990
|
docs.push({
|
|
18772
18991
|
frontmatter: data,
|
|
@@ -18860,7 +19079,7 @@ function planFromSingleDocument(classification, store, _marvinDir, options) {
|
|
|
18860
19079
|
const filePath = classification.inputPath;
|
|
18861
19080
|
const knownTypes = store.registeredTypes;
|
|
18862
19081
|
const raw = fs15.readFileSync(filePath, "utf-8");
|
|
18863
|
-
const { data, content } =
|
|
19082
|
+
const { data, content } = matter5(raw);
|
|
18864
19083
|
if (!isValidMarvinDocument(data, knownTypes)) {
|
|
18865
19084
|
return [];
|
|
18866
19085
|
}
|
|
@@ -19253,13 +19472,11 @@ Analyzing meeting: ${meetingDoc.frontmatter.title}`));
|
|
|
19253
19472
|
}
|
|
19254
19473
|
|
|
19255
19474
|
// src/cli/program.ts
|
|
19256
|
-
var require2 = createRequire(import.meta.url);
|
|
19257
|
-
var { version: version2 } = require2("../../package.json");
|
|
19258
19475
|
function createProgram() {
|
|
19259
19476
|
const program2 = new Command();
|
|
19260
19477
|
program2.name("marvin").description(
|
|
19261
19478
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
19262
|
-
).version(
|
|
19479
|
+
).version("0.2.0");
|
|
19263
19480
|
program2.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
19264
19481
|
await initCommand();
|
|
19265
19482
|
});
|
|
@@ -19320,9 +19537,12 @@ function createProgram() {
|
|
|
19320
19537
|
skillsCmd.command("remove <skill>").description("Unassign a skill from a persona").requiredOption("--as <persona>", "Persona to remove the skill from").action(async (skill, options) => {
|
|
19321
19538
|
await skillsRemoveCommand(skill, options);
|
|
19322
19539
|
});
|
|
19323
|
-
skillsCmd.command("create <name>").description("Create a
|
|
19540
|
+
skillsCmd.command("create <name>").description("Create a new skill in .marvin/skills/ (SKILL.md format)").action(async (name) => {
|
|
19324
19541
|
await skillsCreateCommand(name);
|
|
19325
19542
|
});
|
|
19543
|
+
skillsCmd.command("migrate").description("Migrate YAML skill files to SKILL.md directory format").action(async () => {
|
|
19544
|
+
await skillsMigrateCommand();
|
|
19545
|
+
});
|
|
19326
19546
|
return program2;
|
|
19327
19547
|
}
|
|
19328
19548
|
|