mrvn-cli 0.1.4 → 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 +343 -117
- 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 +342 -119
- 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
|
@@ -15927,7 +15927,7 @@ import * as readline from "readline";
|
|
|
15927
15927
|
import chalk2 from "chalk";
|
|
15928
15928
|
import ora from "ora";
|
|
15929
15929
|
import {
|
|
15930
|
-
query as
|
|
15930
|
+
query as query2
|
|
15931
15931
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
15932
15932
|
|
|
15933
15933
|
// src/personas/prompt-builder.ts
|
|
@@ -16976,7 +16976,9 @@ var SourceManifestManager = class {
|
|
|
16976
16976
|
// src/skills/registry.ts
|
|
16977
16977
|
import * as fs8 from "fs";
|
|
16978
16978
|
import * as path8 from "path";
|
|
16979
|
+
import { fileURLToPath } from "url";
|
|
16979
16980
|
import * as YAML6 from "yaml";
|
|
16981
|
+
import matter2 from "gray-matter";
|
|
16980
16982
|
|
|
16981
16983
|
// src/skills/builtin/governance-review.ts
|
|
16982
16984
|
var governanceReviewSkill = {
|
|
@@ -16984,6 +16986,7 @@ var governanceReviewSkill = {
|
|
|
16984
16986
|
name: "Governance Review",
|
|
16985
16987
|
description: "Review open governance items and generate summaries",
|
|
16986
16988
|
version: "1.0.0",
|
|
16989
|
+
format: "builtin-ts",
|
|
16987
16990
|
personas: ["delivery-manager", "product-owner"],
|
|
16988
16991
|
promptFragments: {
|
|
16989
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.`,
|
|
@@ -17018,11 +17021,98 @@ Be thorough but concise. Focus on actionable insights.`,
|
|
|
17018
17021
|
var BUILTIN_SKILLS = {
|
|
17019
17022
|
"governance-review": governanceReviewSkill
|
|
17020
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
|
+
}
|
|
17021
17098
|
function loadAllSkills(marvinDir) {
|
|
17022
17099
|
const skills = /* @__PURE__ */ new Map();
|
|
17023
17100
|
for (const [id, skill] of Object.entries(BUILTIN_SKILLS)) {
|
|
17024
17101
|
skills.set(id, skill);
|
|
17025
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
|
+
}
|
|
17026
17116
|
if (marvinDir) {
|
|
17027
17117
|
const skillsDir = path8.join(marvinDir, "skills");
|
|
17028
17118
|
if (fs8.existsSync(skillsDir)) {
|
|
@@ -17032,10 +17122,20 @@ function loadAllSkills(marvinDir) {
|
|
|
17032
17122
|
} catch {
|
|
17033
17123
|
entries = [];
|
|
17034
17124
|
}
|
|
17035
|
-
for (const
|
|
17036
|
-
|
|
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;
|
|
17037
17137
|
try {
|
|
17038
|
-
const raw = fs8.readFileSync(
|
|
17138
|
+
const raw = fs8.readFileSync(entryPath, "utf-8");
|
|
17039
17139
|
const parsed = YAML6.parse(raw);
|
|
17040
17140
|
if (!parsed?.id || !parsed?.name || !parsed?.version) continue;
|
|
17041
17141
|
const skill = {
|
|
@@ -17043,6 +17143,7 @@ function loadAllSkills(marvinDir) {
|
|
|
17043
17143
|
name: parsed.name,
|
|
17044
17144
|
description: parsed.description ?? "",
|
|
17045
17145
|
version: parsed.version,
|
|
17146
|
+
format: "yaml",
|
|
17046
17147
|
personas: parsed.personas,
|
|
17047
17148
|
promptFragments: parsed.promptFragments,
|
|
17048
17149
|
actions: parsed.actions
|
|
@@ -17105,91 +17206,73 @@ function listAllSkillInfo(allSkills, skillsConfig, personaIds) {
|
|
|
17105
17206
|
name: skill.name,
|
|
17106
17207
|
version: skill.version,
|
|
17107
17208
|
description: skill.description,
|
|
17209
|
+
format: skill.format,
|
|
17108
17210
|
assignedPersonas
|
|
17109
17211
|
});
|
|
17110
17212
|
}
|
|
17111
17213
|
return result;
|
|
17112
17214
|
}
|
|
17113
|
-
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
|
|
17117
|
-
|
|
17118
|
-
|
|
17119
|
-
|
|
17120
|
-
|
|
17121
|
-
|
|
17122
|
-
|
|
17123
|
-
"mcp__marvin-governance__update_decision",
|
|
17124
|
-
"mcp__marvin-governance__list_actions",
|
|
17125
|
-
"mcp__marvin-governance__get_action",
|
|
17126
|
-
"mcp__marvin-governance__create_action",
|
|
17127
|
-
"mcp__marvin-governance__update_action",
|
|
17128
|
-
"mcp__marvin-governance__list_questions",
|
|
17129
|
-
"mcp__marvin-governance__get_question",
|
|
17130
|
-
"mcp__marvin-governance__create_question",
|
|
17131
|
-
"mcp__marvin-governance__update_question",
|
|
17132
|
-
"mcp__marvin-governance__search_documents",
|
|
17133
|
-
"mcp__marvin-governance__read_document",
|
|
17134
|
-
"mcp__marvin-governance__project_summary"
|
|
17135
|
-
];
|
|
17136
|
-
async function runSkillAction(action, userPrompt, context) {
|
|
17137
|
-
try {
|
|
17138
|
-
const mcpServer = createMarvinMcpServer(context.store);
|
|
17139
|
-
const allowedTools = action.allowGovernanceTools !== false ? GOVERNANCE_TOOL_NAMES : [];
|
|
17140
|
-
const conversation = query2({
|
|
17141
|
-
prompt: userPrompt,
|
|
17142
|
-
options: {
|
|
17143
|
-
systemPrompt: action.systemPrompt,
|
|
17144
|
-
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,
|
|
17145
17225
|
maxTurns: action.maxTurns ?? 5,
|
|
17146
|
-
|
|
17147
|
-
|
|
17148
|
-
|
|
17149
|
-
|
|
17150
|
-
|
|
17151
|
-
|
|
17152
|
-
|
|
17153
|
-
|
|
17154
|
-
|
|
17155
|
-
|
|
17156
|
-
|
|
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"
|
|
17157
17264
|
);
|
|
17158
|
-
for (const block of textBlocks) {
|
|
17159
|
-
textParts.push(block.text);
|
|
17160
|
-
}
|
|
17161
17265
|
}
|
|
17162
17266
|
}
|
|
17163
|
-
return {
|
|
17164
|
-
content: [{ type: "text", text: textParts.join("\n") || "Action completed with no output." }]
|
|
17165
|
-
};
|
|
17166
|
-
} catch (err) {
|
|
17167
|
-
return {
|
|
17168
|
-
content: [{ type: "text", text: `Skill action failed: ${err}` }],
|
|
17169
|
-
isError: true
|
|
17170
|
-
};
|
|
17171
17267
|
}
|
|
17172
|
-
|
|
17173
|
-
|
|
17174
|
-
|
|
17175
|
-
|
|
17176
|
-
|
|
17177
|
-
|
|
17178
|
-
|
|
17179
|
-
for (const action of skill.actions) {
|
|
17180
|
-
tools.push(
|
|
17181
|
-
tool16(
|
|
17182
|
-
`${skill.id}__${action.id}`,
|
|
17183
|
-
action.description,
|
|
17184
|
-
{
|
|
17185
|
-
prompt: external_exports.string().describe("What you want this action to do")
|
|
17186
|
-
},
|
|
17187
|
-
async (args) => runSkillAction(action, args.prompt, context)
|
|
17188
|
-
)
|
|
17189
|
-
);
|
|
17190
|
-
}
|
|
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
|
+
);
|
|
17191
17275
|
}
|
|
17192
|
-
return tools;
|
|
17193
17276
|
}
|
|
17194
17277
|
|
|
17195
17278
|
// src/agent/session.ts
|
|
@@ -17207,15 +17290,14 @@ async function startSession(options) {
|
|
|
17207
17290
|
const allSkills = loadAllSkills(marvinDir);
|
|
17208
17291
|
const skillIds = resolveSkillsForPersona(persona.id, config2.project.skills, allSkills);
|
|
17209
17292
|
const codeSkillTools = getSkillTools(skillIds, allSkills, store);
|
|
17210
|
-
const
|
|
17211
|
-
const actionTools = createSkillActionTools(skillsWithActions, { store, marvinDir, projectRoot });
|
|
17293
|
+
const skillAgents = getSkillAgentDefinitions(skillIds, allSkills);
|
|
17212
17294
|
const skillPromptFragment = getSkillPromptFragment(skillIds, allSkills, persona.id);
|
|
17213
17295
|
const mcpServer = createMarvinMcpServer(store, {
|
|
17214
17296
|
manifest,
|
|
17215
17297
|
sourcesDir: hasSourcesDir ? sourcesDir : void 0,
|
|
17216
17298
|
sessionStore,
|
|
17217
17299
|
pluginTools,
|
|
17218
|
-
skillTools:
|
|
17300
|
+
skillTools: codeSkillTools
|
|
17219
17301
|
});
|
|
17220
17302
|
const systemPrompt = buildSystemPrompt(persona, config2.project, pluginPromptFragment, skillPromptFragment);
|
|
17221
17303
|
let existingSession;
|
|
@@ -17305,14 +17387,16 @@ Marvin \u2014 ${persona.name}
|
|
|
17305
17387
|
"mcp__marvin-governance__get_session",
|
|
17306
17388
|
"mcp__marvin-governance__analyze_meeting",
|
|
17307
17389
|
...pluginTools.map((t) => `mcp__marvin-governance__${t.name}`),
|
|
17308
|
-
...codeSkillTools.map((t) => `mcp__marvin-governance__${t.name}`)
|
|
17309
|
-
...actionTools.map((t) => `mcp__marvin-governance__${t.name}`)
|
|
17390
|
+
...codeSkillTools.map((t) => `mcp__marvin-governance__${t.name}`)
|
|
17310
17391
|
]
|
|
17311
17392
|
};
|
|
17393
|
+
if (Object.keys(skillAgents).length > 0) {
|
|
17394
|
+
queryOptions.agents = skillAgents;
|
|
17395
|
+
}
|
|
17312
17396
|
if (existingSession) {
|
|
17313
17397
|
queryOptions.resume = existingSession.id;
|
|
17314
17398
|
}
|
|
17315
|
-
const conversation =
|
|
17399
|
+
const conversation = query2({
|
|
17316
17400
|
prompt,
|
|
17317
17401
|
options: queryOptions
|
|
17318
17402
|
});
|
|
@@ -17634,7 +17718,7 @@ import * as fs10 from "fs";
|
|
|
17634
17718
|
import * as path10 from "path";
|
|
17635
17719
|
import chalk7 from "chalk";
|
|
17636
17720
|
import ora2 from "ora";
|
|
17637
|
-
import { query as
|
|
17721
|
+
import { query as query3 } from "@anthropic-ai/claude-agent-sdk";
|
|
17638
17722
|
|
|
17639
17723
|
// src/sources/prompts.ts
|
|
17640
17724
|
function buildIngestSystemPrompt(persona, projectConfig, isDraft) {
|
|
@@ -17767,7 +17851,7 @@ async function ingestFile(options) {
|
|
|
17767
17851
|
const spinner = ora2({ text: `Analyzing ${fileName}...`, color: "cyan" });
|
|
17768
17852
|
spinner.start();
|
|
17769
17853
|
try {
|
|
17770
|
-
const conversation =
|
|
17854
|
+
const conversation = query3({
|
|
17771
17855
|
prompt: userPrompt,
|
|
17772
17856
|
options: {
|
|
17773
17857
|
systemPrompt,
|
|
@@ -17986,8 +18070,8 @@ var MarvinGit = class {
|
|
|
17986
18070
|
);
|
|
17987
18071
|
}
|
|
17988
18072
|
await this.git.init();
|
|
17989
|
-
const { writeFileSync:
|
|
17990
|
-
|
|
18073
|
+
const { writeFileSync: writeFileSync9 } = await import("fs");
|
|
18074
|
+
writeFileSync9(
|
|
17991
18075
|
path12.join(this.marvinDir, ".gitignore"),
|
|
17992
18076
|
MARVIN_GITIGNORE,
|
|
17993
18077
|
"utf-8"
|
|
@@ -18292,6 +18376,89 @@ import * as fs12 from "fs";
|
|
|
18292
18376
|
import * as path13 from "path";
|
|
18293
18377
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
18294
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
|
|
18295
18462
|
function collectTools(marvinDir) {
|
|
18296
18463
|
const config2 = loadProjectConfig(marvinDir);
|
|
18297
18464
|
const plugin = resolvePlugin(config2.methodology);
|
|
@@ -18369,6 +18536,7 @@ async function serveCommand() {
|
|
|
18369
18536
|
import * as fs13 from "fs";
|
|
18370
18537
|
import * as path14 from "path";
|
|
18371
18538
|
import * as YAML7 from "yaml";
|
|
18539
|
+
import matter3 from "gray-matter";
|
|
18372
18540
|
import chalk10 from "chalk";
|
|
18373
18541
|
async function skillsListCommand() {
|
|
18374
18542
|
const project = loadProject();
|
|
@@ -18382,10 +18550,12 @@ async function skillsListCommand() {
|
|
|
18382
18550
|
}
|
|
18383
18551
|
console.log(chalk10.bold("\nAvailable Skills\n"));
|
|
18384
18552
|
const idWidth = Math.max(5, ...infos.map((s) => s.id.length));
|
|
18553
|
+
const fmtWidth = Math.max(6, ...infos.map((s) => s.format.length));
|
|
18385
18554
|
const verWidth = Math.max(7, ...infos.map((s) => s.version.length));
|
|
18386
18555
|
const descWidth = Math.max(11, ...infos.map((s) => s.description.length));
|
|
18387
18556
|
const header = [
|
|
18388
18557
|
"ID".padEnd(idWidth),
|
|
18558
|
+
"Format".padEnd(fmtWidth),
|
|
18389
18559
|
"Version".padEnd(verWidth),
|
|
18390
18560
|
"Description".padEnd(descWidth),
|
|
18391
18561
|
"Personas"
|
|
@@ -18397,6 +18567,7 @@ async function skillsListCommand() {
|
|
|
18397
18567
|
console.log(
|
|
18398
18568
|
[
|
|
18399
18569
|
info.id.padEnd(idWidth),
|
|
18570
|
+
info.format.padEnd(fmtWidth),
|
|
18400
18571
|
info.version.padEnd(verWidth),
|
|
18401
18572
|
info.description.padEnd(descWidth),
|
|
18402
18573
|
personas
|
|
@@ -18465,33 +18636,82 @@ async function skillsCreateCommand(name) {
|
|
|
18465
18636
|
const project = loadProject();
|
|
18466
18637
|
const skillsDir = path14.join(project.marvinDir, "skills");
|
|
18467
18638
|
fs13.mkdirSync(skillsDir, { recursive: true });
|
|
18468
|
-
const
|
|
18469
|
-
if (fs13.existsSync(
|
|
18470
|
-
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}`));
|
|
18471
18642
|
return;
|
|
18472
18643
|
}
|
|
18473
|
-
|
|
18474
|
-
|
|
18475
|
-
|
|
18644
|
+
fs13.mkdirSync(skillDir, { recursive: true });
|
|
18645
|
+
const displayName = name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
18646
|
+
const frontmatter = {
|
|
18647
|
+
name,
|
|
18476
18648
|
description: `Custom skill: ${name}`,
|
|
18477
|
-
|
|
18478
|
-
|
|
18479
|
-
|
|
18480
|
-
|
|
18481
|
-
},
|
|
18482
|
-
actions: [
|
|
18483
|
-
{
|
|
18484
|
-
id: "run",
|
|
18485
|
-
name: `Run ${name}`,
|
|
18486
|
-
description: `Execute the ${name} skill`,
|
|
18487
|
-
systemPrompt: "You are a helpful assistant. Complete the requested task using the available governance tools.",
|
|
18488
|
-
maxTurns: 5
|
|
18489
|
-
}
|
|
18490
|
-
]
|
|
18649
|
+
metadata: {
|
|
18650
|
+
version: "1.0.0",
|
|
18651
|
+
personas: ["product-owner"]
|
|
18652
|
+
}
|
|
18491
18653
|
};
|
|
18492
|
-
|
|
18493
|
-
|
|
18494
|
-
|
|
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
|
+
}
|
|
18495
18715
|
}
|
|
18496
18716
|
|
|
18497
18717
|
// src/cli/commands/import.ts
|
|
@@ -18502,12 +18722,12 @@ import chalk11 from "chalk";
|
|
|
18502
18722
|
// src/import/engine.ts
|
|
18503
18723
|
import * as fs15 from "fs";
|
|
18504
18724
|
import * as path16 from "path";
|
|
18505
|
-
import
|
|
18725
|
+
import matter5 from "gray-matter";
|
|
18506
18726
|
|
|
18507
18727
|
// src/import/classifier.ts
|
|
18508
18728
|
import * as fs14 from "fs";
|
|
18509
18729
|
import * as path15 from "path";
|
|
18510
|
-
import
|
|
18730
|
+
import matter4 from "gray-matter";
|
|
18511
18731
|
var RAW_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".pdf", ".txt"]);
|
|
18512
18732
|
var CORE_DIR_NAMES = /* @__PURE__ */ new Set(["decisions", "actions", "questions"]);
|
|
18513
18733
|
var ID_PATTERN = /^[A-Z]+-\d{3,}$/;
|
|
@@ -18533,7 +18753,7 @@ function classifyPath(inputPath, knownTypes, knownDirNames) {
|
|
|
18533
18753
|
const hasMarvinDocs = mdFiles.some((f) => {
|
|
18534
18754
|
try {
|
|
18535
18755
|
const raw = fs14.readFileSync(path15.join(resolved, f), "utf-8");
|
|
18536
|
-
const { data } =
|
|
18756
|
+
const { data } = matter4(raw);
|
|
18537
18757
|
return isValidMarvinDocument(data, knownTypes);
|
|
18538
18758
|
} catch {
|
|
18539
18759
|
return false;
|
|
@@ -18554,7 +18774,7 @@ function classifyFile(filePath, knownTypes) {
|
|
|
18554
18774
|
if (ext === ".md") {
|
|
18555
18775
|
try {
|
|
18556
18776
|
const raw = fs14.readFileSync(resolved, "utf-8");
|
|
18557
|
-
const { data } =
|
|
18777
|
+
const { data } = matter4(raw);
|
|
18558
18778
|
if (isValidMarvinDocument(data, knownTypes)) {
|
|
18559
18779
|
return { type: "marvin-document", inputPath: resolved };
|
|
18560
18780
|
}
|
|
@@ -18765,7 +18985,7 @@ function collectMarvinDocs(dir, knownTypes) {
|
|
|
18765
18985
|
const filePath = path16.join(dir, file2);
|
|
18766
18986
|
try {
|
|
18767
18987
|
const raw = fs15.readFileSync(filePath, "utf-8");
|
|
18768
|
-
const { data, content } =
|
|
18988
|
+
const { data, content } = matter5(raw);
|
|
18769
18989
|
if (isValidMarvinDocument(data, knownTypes)) {
|
|
18770
18990
|
docs.push({
|
|
18771
18991
|
frontmatter: data,
|
|
@@ -18859,7 +19079,7 @@ function planFromSingleDocument(classification, store, _marvinDir, options) {
|
|
|
18859
19079
|
const filePath = classification.inputPath;
|
|
18860
19080
|
const knownTypes = store.registeredTypes;
|
|
18861
19081
|
const raw = fs15.readFileSync(filePath, "utf-8");
|
|
18862
|
-
const { data, content } =
|
|
19082
|
+
const { data, content } = matter5(raw);
|
|
18863
19083
|
if (!isValidMarvinDocument(data, knownTypes)) {
|
|
18864
19084
|
return [];
|
|
18865
19085
|
}
|
|
@@ -19256,7 +19476,7 @@ function createProgram() {
|
|
|
19256
19476
|
const program2 = new Command();
|
|
19257
19477
|
program2.name("marvin").description(
|
|
19258
19478
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
19259
|
-
).version("0.
|
|
19479
|
+
).version("0.2.0");
|
|
19260
19480
|
program2.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
19261
19481
|
await initCommand();
|
|
19262
19482
|
});
|
|
@@ -19317,9 +19537,12 @@ function createProgram() {
|
|
|
19317
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) => {
|
|
19318
19538
|
await skillsRemoveCommand(skill, options);
|
|
19319
19539
|
});
|
|
19320
|
-
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) => {
|
|
19321
19541
|
await skillsCreateCommand(name);
|
|
19322
19542
|
});
|
|
19543
|
+
skillsCmd.command("migrate").description("Migrate YAML skill files to SKILL.md directory format").action(async () => {
|
|
19544
|
+
await skillsMigrateCommand();
|
|
19545
|
+
});
|
|
19323
19546
|
return program2;
|
|
19324
19547
|
}
|
|
19325
19548
|
|