mrvn-cli 0.1.4 → 0.2.1
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 +555 -119
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +458 -5
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +554 -121
- 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/index.js
CHANGED
|
@@ -14902,7 +14902,7 @@ import * as readline from "readline";
|
|
|
14902
14902
|
import chalk from "chalk";
|
|
14903
14903
|
import ora from "ora";
|
|
14904
14904
|
import {
|
|
14905
|
-
query as
|
|
14905
|
+
query as query2
|
|
14906
14906
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
14907
14907
|
|
|
14908
14908
|
// src/storage/session-store.ts
|
|
@@ -16863,7 +16863,9 @@ function getPluginPromptFragment(plugin, personaId) {
|
|
|
16863
16863
|
// src/skills/registry.ts
|
|
16864
16864
|
import * as fs7 from "fs";
|
|
16865
16865
|
import * as path7 from "path";
|
|
16866
|
+
import { fileURLToPath } from "url";
|
|
16866
16867
|
import * as YAML5 from "yaml";
|
|
16868
|
+
import matter2 from "gray-matter";
|
|
16867
16869
|
|
|
16868
16870
|
// src/skills/builtin/governance-review.ts
|
|
16869
16871
|
var governanceReviewSkill = {
|
|
@@ -16871,6 +16873,7 @@ var governanceReviewSkill = {
|
|
|
16871
16873
|
name: "Governance Review",
|
|
16872
16874
|
description: "Review open governance items and generate summaries",
|
|
16873
16875
|
version: "1.0.0",
|
|
16876
|
+
format: "builtin-ts",
|
|
16874
16877
|
personas: ["delivery-manager", "product-owner"],
|
|
16875
16878
|
promptFragments: {
|
|
16876
16879
|
"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.`,
|
|
@@ -16905,11 +16908,98 @@ Be thorough but concise. Focus on actionable insights.`,
|
|
|
16905
16908
|
var BUILTIN_SKILLS = {
|
|
16906
16909
|
"governance-review": governanceReviewSkill
|
|
16907
16910
|
};
|
|
16911
|
+
var GOVERNANCE_TOOL_NAMES = [
|
|
16912
|
+
"mcp__marvin-governance__list_decisions",
|
|
16913
|
+
"mcp__marvin-governance__get_decision",
|
|
16914
|
+
"mcp__marvin-governance__create_decision",
|
|
16915
|
+
"mcp__marvin-governance__update_decision",
|
|
16916
|
+
"mcp__marvin-governance__list_actions",
|
|
16917
|
+
"mcp__marvin-governance__get_action",
|
|
16918
|
+
"mcp__marvin-governance__create_action",
|
|
16919
|
+
"mcp__marvin-governance__update_action",
|
|
16920
|
+
"mcp__marvin-governance__list_questions",
|
|
16921
|
+
"mcp__marvin-governance__get_question",
|
|
16922
|
+
"mcp__marvin-governance__create_question",
|
|
16923
|
+
"mcp__marvin-governance__update_question",
|
|
16924
|
+
"mcp__marvin-governance__search_documents",
|
|
16925
|
+
"mcp__marvin-governance__read_document",
|
|
16926
|
+
"mcp__marvin-governance__project_summary"
|
|
16927
|
+
];
|
|
16928
|
+
function getBuiltinSkillsDir() {
|
|
16929
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
16930
|
+
return path7.join(path7.dirname(thisFile), "builtin");
|
|
16931
|
+
}
|
|
16932
|
+
function loadSkillFromDirectory(dirPath) {
|
|
16933
|
+
const skillMdPath = path7.join(dirPath, "SKILL.md");
|
|
16934
|
+
if (!fs7.existsSync(skillMdPath)) return void 0;
|
|
16935
|
+
try {
|
|
16936
|
+
const raw = fs7.readFileSync(skillMdPath, "utf-8");
|
|
16937
|
+
const { data, content } = matter2(raw);
|
|
16938
|
+
if (!data.name || !data.description) return void 0;
|
|
16939
|
+
const metadata = data.metadata ?? {};
|
|
16940
|
+
const version2 = metadata.version ?? "1.0.0";
|
|
16941
|
+
const personas = metadata.personas;
|
|
16942
|
+
const promptFragments = {};
|
|
16943
|
+
const wildcardPrompt = content.trim();
|
|
16944
|
+
if (wildcardPrompt) {
|
|
16945
|
+
promptFragments["*"] = wildcardPrompt;
|
|
16946
|
+
}
|
|
16947
|
+
const personasDir = path7.join(dirPath, "personas");
|
|
16948
|
+
if (fs7.existsSync(personasDir)) {
|
|
16949
|
+
try {
|
|
16950
|
+
for (const file2 of fs7.readdirSync(personasDir)) {
|
|
16951
|
+
if (!file2.endsWith(".md")) continue;
|
|
16952
|
+
const personaId = file2.replace(/\.md$/, "");
|
|
16953
|
+
const personaPrompt = fs7.readFileSync(path7.join(personasDir, file2), "utf-8").trim();
|
|
16954
|
+
if (personaPrompt) {
|
|
16955
|
+
promptFragments[personaId] = personaPrompt;
|
|
16956
|
+
}
|
|
16957
|
+
}
|
|
16958
|
+
} catch {
|
|
16959
|
+
}
|
|
16960
|
+
}
|
|
16961
|
+
let actions;
|
|
16962
|
+
const actionsPath = path7.join(dirPath, "actions.yaml");
|
|
16963
|
+
if (fs7.existsSync(actionsPath)) {
|
|
16964
|
+
try {
|
|
16965
|
+
const actionsRaw = fs7.readFileSync(actionsPath, "utf-8");
|
|
16966
|
+
actions = YAML5.parse(actionsRaw);
|
|
16967
|
+
} catch {
|
|
16968
|
+
}
|
|
16969
|
+
}
|
|
16970
|
+
return {
|
|
16971
|
+
id: data.name,
|
|
16972
|
+
name: data.name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
16973
|
+
description: data.description,
|
|
16974
|
+
version: version2,
|
|
16975
|
+
format: "skill-md",
|
|
16976
|
+
dirPath,
|
|
16977
|
+
personas,
|
|
16978
|
+
promptFragments: Object.keys(promptFragments).length > 0 ? promptFragments : void 0,
|
|
16979
|
+
actions
|
|
16980
|
+
};
|
|
16981
|
+
} catch {
|
|
16982
|
+
return void 0;
|
|
16983
|
+
}
|
|
16984
|
+
}
|
|
16908
16985
|
function loadAllSkills(marvinDir) {
|
|
16909
16986
|
const skills = /* @__PURE__ */ new Map();
|
|
16910
16987
|
for (const [id, skill] of Object.entries(BUILTIN_SKILLS)) {
|
|
16911
16988
|
skills.set(id, skill);
|
|
16912
16989
|
}
|
|
16990
|
+
try {
|
|
16991
|
+
const builtinDir = getBuiltinSkillsDir();
|
|
16992
|
+
if (fs7.existsSync(builtinDir)) {
|
|
16993
|
+
for (const entry of fs7.readdirSync(builtinDir)) {
|
|
16994
|
+
const entryPath = path7.join(builtinDir, entry);
|
|
16995
|
+
if (!fs7.statSync(entryPath).isDirectory()) continue;
|
|
16996
|
+
if (skills.has(entry)) continue;
|
|
16997
|
+
const skill = loadSkillFromDirectory(entryPath);
|
|
16998
|
+
if (skill) skills.set(skill.id, skill);
|
|
16999
|
+
}
|
|
17000
|
+
}
|
|
17001
|
+
} catch {
|
|
17002
|
+
}
|
|
16913
17003
|
if (marvinDir) {
|
|
16914
17004
|
const skillsDir = path7.join(marvinDir, "skills");
|
|
16915
17005
|
if (fs7.existsSync(skillsDir)) {
|
|
@@ -16919,10 +17009,20 @@ function loadAllSkills(marvinDir) {
|
|
|
16919
17009
|
} catch {
|
|
16920
17010
|
entries = [];
|
|
16921
17011
|
}
|
|
16922
|
-
for (const
|
|
16923
|
-
|
|
17012
|
+
for (const entry of entries) {
|
|
17013
|
+
const entryPath = path7.join(skillsDir, entry);
|
|
16924
17014
|
try {
|
|
16925
|
-
|
|
17015
|
+
if (fs7.statSync(entryPath).isDirectory()) {
|
|
17016
|
+
const skill = loadSkillFromDirectory(entryPath);
|
|
17017
|
+
if (skill) skills.set(skill.id, skill);
|
|
17018
|
+
continue;
|
|
17019
|
+
}
|
|
17020
|
+
} catch {
|
|
17021
|
+
continue;
|
|
17022
|
+
}
|
|
17023
|
+
if (!entry.endsWith(".yaml") && !entry.endsWith(".yml")) continue;
|
|
17024
|
+
try {
|
|
17025
|
+
const raw = fs7.readFileSync(entryPath, "utf-8");
|
|
16926
17026
|
const parsed = YAML5.parse(raw);
|
|
16927
17027
|
if (!parsed?.id || !parsed?.name || !parsed?.version) continue;
|
|
16928
17028
|
const skill = {
|
|
@@ -16930,6 +17030,7 @@ function loadAllSkills(marvinDir) {
|
|
|
16930
17030
|
name: parsed.name,
|
|
16931
17031
|
description: parsed.description ?? "",
|
|
16932
17032
|
version: parsed.version,
|
|
17033
|
+
format: "yaml",
|
|
16933
17034
|
personas: parsed.personas,
|
|
16934
17035
|
promptFragments: parsed.promptFragments,
|
|
16935
17036
|
actions: parsed.actions
|
|
@@ -16992,91 +17093,73 @@ function listAllSkillInfo(allSkills, skillsConfig, personaIds) {
|
|
|
16992
17093
|
name: skill.name,
|
|
16993
17094
|
version: skill.version,
|
|
16994
17095
|
description: skill.description,
|
|
17096
|
+
format: skill.format,
|
|
16995
17097
|
assignedPersonas
|
|
16996
17098
|
});
|
|
16997
17099
|
}
|
|
16998
17100
|
return result;
|
|
16999
17101
|
}
|
|
17000
|
-
|
|
17001
|
-
|
|
17002
|
-
|
|
17003
|
-
|
|
17004
|
-
|
|
17005
|
-
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
|
|
17009
|
-
|
|
17010
|
-
"mcp__marvin-governance__update_decision",
|
|
17011
|
-
"mcp__marvin-governance__list_actions",
|
|
17012
|
-
"mcp__marvin-governance__get_action",
|
|
17013
|
-
"mcp__marvin-governance__create_action",
|
|
17014
|
-
"mcp__marvin-governance__update_action",
|
|
17015
|
-
"mcp__marvin-governance__list_questions",
|
|
17016
|
-
"mcp__marvin-governance__get_question",
|
|
17017
|
-
"mcp__marvin-governance__create_question",
|
|
17018
|
-
"mcp__marvin-governance__update_question",
|
|
17019
|
-
"mcp__marvin-governance__search_documents",
|
|
17020
|
-
"mcp__marvin-governance__read_document",
|
|
17021
|
-
"mcp__marvin-governance__project_summary"
|
|
17022
|
-
];
|
|
17023
|
-
async function runSkillAction(action, userPrompt, context) {
|
|
17024
|
-
try {
|
|
17025
|
-
const mcpServer = createMarvinMcpServer(context.store);
|
|
17026
|
-
const allowedTools = action.allowGovernanceTools !== false ? GOVERNANCE_TOOL_NAMES : [];
|
|
17027
|
-
const conversation = query2({
|
|
17028
|
-
prompt: userPrompt,
|
|
17029
|
-
options: {
|
|
17030
|
-
systemPrompt: action.systemPrompt,
|
|
17031
|
-
mcpServers: { "marvin-governance": mcpServer },
|
|
17102
|
+
function getSkillAgentDefinitions(skillIds, allSkills) {
|
|
17103
|
+
const agents = {};
|
|
17104
|
+
for (const id of skillIds) {
|
|
17105
|
+
const skill = allSkills.get(id);
|
|
17106
|
+
if (!skill?.actions) continue;
|
|
17107
|
+
for (const action of skill.actions) {
|
|
17108
|
+
const agentKey = `${skill.id}__${action.id}`;
|
|
17109
|
+
agents[agentKey] = {
|
|
17110
|
+
description: action.description,
|
|
17111
|
+
prompt: action.systemPrompt,
|
|
17032
17112
|
maxTurns: action.maxTurns ?? 5,
|
|
17033
|
-
|
|
17034
|
-
|
|
17035
|
-
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
|
|
17039
|
-
|
|
17040
|
-
|
|
17041
|
-
|
|
17042
|
-
|
|
17043
|
-
|
|
17113
|
+
tools: action.allowGovernanceTools !== false ? GOVERNANCE_TOOL_NAMES : []
|
|
17114
|
+
};
|
|
17115
|
+
}
|
|
17116
|
+
}
|
|
17117
|
+
return agents;
|
|
17118
|
+
}
|
|
17119
|
+
function migrateYamlToSkillMd(yamlPath, outputDir) {
|
|
17120
|
+
const raw = fs7.readFileSync(yamlPath, "utf-8");
|
|
17121
|
+
const parsed = YAML5.parse(raw);
|
|
17122
|
+
if (!parsed?.id || !parsed?.name) {
|
|
17123
|
+
throw new Error(`Invalid skill YAML: missing required fields (id, name)`);
|
|
17124
|
+
}
|
|
17125
|
+
fs7.mkdirSync(outputDir, { recursive: true });
|
|
17126
|
+
const frontmatter = {
|
|
17127
|
+
name: parsed.id,
|
|
17128
|
+
description: parsed.description ?? ""
|
|
17129
|
+
};
|
|
17130
|
+
const metadata = {};
|
|
17131
|
+
if (parsed.version) metadata.version = parsed.version;
|
|
17132
|
+
if (parsed.personas) metadata.personas = parsed.personas;
|
|
17133
|
+
if (Object.keys(metadata).length > 0) frontmatter.metadata = metadata;
|
|
17134
|
+
const promptFragments = parsed.promptFragments;
|
|
17135
|
+
const wildcardPrompt = promptFragments?.["*"] ?? "";
|
|
17136
|
+
const skillMd = matter2.stringify(wildcardPrompt ? `
|
|
17137
|
+
${wildcardPrompt}
|
|
17138
|
+
` : "\n", frontmatter);
|
|
17139
|
+
fs7.writeFileSync(path7.join(outputDir, "SKILL.md"), skillMd, "utf-8");
|
|
17140
|
+
if (promptFragments) {
|
|
17141
|
+
const personaKeys = Object.keys(promptFragments).filter((k) => k !== "*");
|
|
17142
|
+
if (personaKeys.length > 0) {
|
|
17143
|
+
const personasDir = path7.join(outputDir, "personas");
|
|
17144
|
+
fs7.mkdirSync(personasDir, { recursive: true });
|
|
17145
|
+
for (const personaId of personaKeys) {
|
|
17146
|
+
fs7.writeFileSync(
|
|
17147
|
+
path7.join(personasDir, `${personaId}.md`),
|
|
17148
|
+
`${promptFragments[personaId]}
|
|
17149
|
+
`,
|
|
17150
|
+
"utf-8"
|
|
17044
17151
|
);
|
|
17045
|
-
for (const block of textBlocks) {
|
|
17046
|
-
textParts.push(block.text);
|
|
17047
|
-
}
|
|
17048
17152
|
}
|
|
17049
17153
|
}
|
|
17050
|
-
return {
|
|
17051
|
-
content: [{ type: "text", text: textParts.join("\n") || "Action completed with no output." }]
|
|
17052
|
-
};
|
|
17053
|
-
} catch (err) {
|
|
17054
|
-
return {
|
|
17055
|
-
content: [{ type: "text", text: `Skill action failed: ${err}` }],
|
|
17056
|
-
isError: true
|
|
17057
|
-
};
|
|
17058
17154
|
}
|
|
17059
|
-
|
|
17060
|
-
|
|
17061
|
-
|
|
17062
|
-
|
|
17063
|
-
|
|
17064
|
-
|
|
17065
|
-
|
|
17066
|
-
for (const action of skill.actions) {
|
|
17067
|
-
tools.push(
|
|
17068
|
-
tool16(
|
|
17069
|
-
`${skill.id}__${action.id}`,
|
|
17070
|
-
action.description,
|
|
17071
|
-
{
|
|
17072
|
-
prompt: external_exports.string().describe("What you want this action to do")
|
|
17073
|
-
},
|
|
17074
|
-
async (args) => runSkillAction(action, args.prompt, context)
|
|
17075
|
-
)
|
|
17076
|
-
);
|
|
17077
|
-
}
|
|
17155
|
+
const actions = parsed.actions;
|
|
17156
|
+
if (actions && actions.length > 0) {
|
|
17157
|
+
fs7.writeFileSync(
|
|
17158
|
+
path7.join(outputDir, "actions.yaml"),
|
|
17159
|
+
YAML5.stringify(actions),
|
|
17160
|
+
"utf-8"
|
|
17161
|
+
);
|
|
17078
17162
|
}
|
|
17079
|
-
return tools;
|
|
17080
17163
|
}
|
|
17081
17164
|
|
|
17082
17165
|
// src/agent/session.ts
|
|
@@ -17094,15 +17177,14 @@ async function startSession(options) {
|
|
|
17094
17177
|
const allSkills = loadAllSkills(marvinDir);
|
|
17095
17178
|
const skillIds = resolveSkillsForPersona(persona.id, config2.project.skills, allSkills);
|
|
17096
17179
|
const codeSkillTools = getSkillTools(skillIds, allSkills, store);
|
|
17097
|
-
const
|
|
17098
|
-
const actionTools = createSkillActionTools(skillsWithActions, { store, marvinDir, projectRoot });
|
|
17180
|
+
const skillAgents = getSkillAgentDefinitions(skillIds, allSkills);
|
|
17099
17181
|
const skillPromptFragment = getSkillPromptFragment(skillIds, allSkills, persona.id);
|
|
17100
17182
|
const mcpServer = createMarvinMcpServer(store, {
|
|
17101
17183
|
manifest,
|
|
17102
17184
|
sourcesDir: hasSourcesDir ? sourcesDir : void 0,
|
|
17103
17185
|
sessionStore,
|
|
17104
17186
|
pluginTools,
|
|
17105
|
-
skillTools:
|
|
17187
|
+
skillTools: codeSkillTools
|
|
17106
17188
|
});
|
|
17107
17189
|
const systemPrompt = buildSystemPrompt(persona, config2.project, pluginPromptFragment, skillPromptFragment);
|
|
17108
17190
|
let existingSession;
|
|
@@ -17192,14 +17274,16 @@ Marvin \u2014 ${persona.name}
|
|
|
17192
17274
|
"mcp__marvin-governance__get_session",
|
|
17193
17275
|
"mcp__marvin-governance__analyze_meeting",
|
|
17194
17276
|
...pluginTools.map((t) => `mcp__marvin-governance__${t.name}`),
|
|
17195
|
-
...codeSkillTools.map((t) => `mcp__marvin-governance__${t.name}`)
|
|
17196
|
-
...actionTools.map((t) => `mcp__marvin-governance__${t.name}`)
|
|
17277
|
+
...codeSkillTools.map((t) => `mcp__marvin-governance__${t.name}`)
|
|
17197
17278
|
]
|
|
17198
17279
|
};
|
|
17280
|
+
if (Object.keys(skillAgents).length > 0) {
|
|
17281
|
+
queryOptions.agents = skillAgents;
|
|
17282
|
+
}
|
|
17199
17283
|
if (existingSession) {
|
|
17200
17284
|
queryOptions.resume = existingSession.id;
|
|
17201
17285
|
}
|
|
17202
|
-
const conversation =
|
|
17286
|
+
const conversation = query2({
|
|
17203
17287
|
prompt,
|
|
17204
17288
|
options: queryOptions
|
|
17205
17289
|
});
|
|
@@ -17286,6 +17370,296 @@ import * as fs9 from "fs";
|
|
|
17286
17370
|
import * as path9 from "path";
|
|
17287
17371
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
17288
17372
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
17373
|
+
|
|
17374
|
+
// src/skills/action-tools.ts
|
|
17375
|
+
import { tool as tool16 } from "@anthropic-ai/claude-agent-sdk";
|
|
17376
|
+
|
|
17377
|
+
// src/skills/action-runner.ts
|
|
17378
|
+
import { query as query3 } from "@anthropic-ai/claude-agent-sdk";
|
|
17379
|
+
var GOVERNANCE_TOOL_NAMES2 = [
|
|
17380
|
+
"mcp__marvin-governance__list_decisions",
|
|
17381
|
+
"mcp__marvin-governance__get_decision",
|
|
17382
|
+
"mcp__marvin-governance__create_decision",
|
|
17383
|
+
"mcp__marvin-governance__update_decision",
|
|
17384
|
+
"mcp__marvin-governance__list_actions",
|
|
17385
|
+
"mcp__marvin-governance__get_action",
|
|
17386
|
+
"mcp__marvin-governance__create_action",
|
|
17387
|
+
"mcp__marvin-governance__update_action",
|
|
17388
|
+
"mcp__marvin-governance__list_questions",
|
|
17389
|
+
"mcp__marvin-governance__get_question",
|
|
17390
|
+
"mcp__marvin-governance__create_question",
|
|
17391
|
+
"mcp__marvin-governance__update_question",
|
|
17392
|
+
"mcp__marvin-governance__search_documents",
|
|
17393
|
+
"mcp__marvin-governance__read_document",
|
|
17394
|
+
"mcp__marvin-governance__project_summary"
|
|
17395
|
+
];
|
|
17396
|
+
async function runSkillAction(action, userPrompt, context) {
|
|
17397
|
+
try {
|
|
17398
|
+
const mcpServer = createMarvinMcpServer(context.store);
|
|
17399
|
+
const allowedTools = action.allowGovernanceTools !== false ? GOVERNANCE_TOOL_NAMES2 : [];
|
|
17400
|
+
const conversation = query3({
|
|
17401
|
+
prompt: userPrompt,
|
|
17402
|
+
options: {
|
|
17403
|
+
systemPrompt: action.systemPrompt,
|
|
17404
|
+
mcpServers: { "marvin-governance": mcpServer },
|
|
17405
|
+
maxTurns: action.maxTurns ?? 5,
|
|
17406
|
+
allowedTools,
|
|
17407
|
+
cwd: context.projectRoot,
|
|
17408
|
+
tools: [],
|
|
17409
|
+
permissionMode: "acceptEdits"
|
|
17410
|
+
}
|
|
17411
|
+
});
|
|
17412
|
+
const textParts = [];
|
|
17413
|
+
for await (const message of conversation) {
|
|
17414
|
+
if (message.type === "assistant") {
|
|
17415
|
+
const textBlocks = message.message.content.filter(
|
|
17416
|
+
(b) => b.type === "text"
|
|
17417
|
+
);
|
|
17418
|
+
for (const block of textBlocks) {
|
|
17419
|
+
textParts.push(block.text);
|
|
17420
|
+
}
|
|
17421
|
+
}
|
|
17422
|
+
}
|
|
17423
|
+
return {
|
|
17424
|
+
content: [{ type: "text", text: textParts.join("\n") || "Action completed with no output." }]
|
|
17425
|
+
};
|
|
17426
|
+
} catch (err) {
|
|
17427
|
+
return {
|
|
17428
|
+
content: [{ type: "text", text: `Skill action failed: ${err}` }],
|
|
17429
|
+
isError: true
|
|
17430
|
+
};
|
|
17431
|
+
}
|
|
17432
|
+
}
|
|
17433
|
+
|
|
17434
|
+
// src/skills/action-tools.ts
|
|
17435
|
+
function createSkillActionTools(skills, context) {
|
|
17436
|
+
const tools = [];
|
|
17437
|
+
for (const skill of skills) {
|
|
17438
|
+
if (!skill.actions) continue;
|
|
17439
|
+
for (const action of skill.actions) {
|
|
17440
|
+
tools.push(
|
|
17441
|
+
tool16(
|
|
17442
|
+
`${skill.id}__${action.id}`,
|
|
17443
|
+
action.description,
|
|
17444
|
+
{
|
|
17445
|
+
prompt: external_exports.string().describe("What you want this action to do")
|
|
17446
|
+
},
|
|
17447
|
+
async (args) => runSkillAction(action, args.prompt, context)
|
|
17448
|
+
)
|
|
17449
|
+
);
|
|
17450
|
+
}
|
|
17451
|
+
}
|
|
17452
|
+
return tools;
|
|
17453
|
+
}
|
|
17454
|
+
|
|
17455
|
+
// src/mcp/persona-context.ts
|
|
17456
|
+
var PersonaContextManager = class {
|
|
17457
|
+
activePersona = null;
|
|
17458
|
+
setPersona(idOrShortName) {
|
|
17459
|
+
const persona = getPersona(idOrShortName);
|
|
17460
|
+
if (persona) {
|
|
17461
|
+
this.activePersona = persona;
|
|
17462
|
+
}
|
|
17463
|
+
return persona;
|
|
17464
|
+
}
|
|
17465
|
+
getActivePersona() {
|
|
17466
|
+
return this.activePersona;
|
|
17467
|
+
}
|
|
17468
|
+
clearPersona() {
|
|
17469
|
+
this.activePersona = null;
|
|
17470
|
+
}
|
|
17471
|
+
isDocumentTypeAllowed(docType) {
|
|
17472
|
+
if (!this.activePersona) return true;
|
|
17473
|
+
return this.activePersona.documentTypes.includes(docType);
|
|
17474
|
+
}
|
|
17475
|
+
};
|
|
17476
|
+
function buildMcpGuidance(persona, marvinDir) {
|
|
17477
|
+
const parts = [];
|
|
17478
|
+
parts.push(`# Active Persona: ${persona.name} (${persona.shortName})`);
|
|
17479
|
+
parts.push(`
|
|
17480
|
+
${persona.description}`);
|
|
17481
|
+
parts.push(`
|
|
17482
|
+
## Focus Areas`);
|
|
17483
|
+
for (const area of persona.focusAreas) {
|
|
17484
|
+
parts.push(`- ${area}`);
|
|
17485
|
+
}
|
|
17486
|
+
parts.push(`
|
|
17487
|
+
## Allowed Document Types`);
|
|
17488
|
+
parts.push(persona.documentTypes.join(", "));
|
|
17489
|
+
parts.push(`
|
|
17490
|
+
## Behavioral Instructions`);
|
|
17491
|
+
parts.push(persona.systemPrompt);
|
|
17492
|
+
try {
|
|
17493
|
+
const config2 = loadProjectConfig(marvinDir);
|
|
17494
|
+
const plugin = resolvePlugin(config2.methodology);
|
|
17495
|
+
if (plugin) {
|
|
17496
|
+
const fragment = getPluginPromptFragment(plugin, persona.id);
|
|
17497
|
+
if (fragment) {
|
|
17498
|
+
parts.push(`
|
|
17499
|
+
## Plugin Rules`);
|
|
17500
|
+
parts.push(fragment);
|
|
17501
|
+
}
|
|
17502
|
+
}
|
|
17503
|
+
const allSkills = loadAllSkills(marvinDir);
|
|
17504
|
+
const skillIds = resolveSkillsForPersona(
|
|
17505
|
+
persona.id,
|
|
17506
|
+
config2.skills,
|
|
17507
|
+
allSkills
|
|
17508
|
+
);
|
|
17509
|
+
if (skillIds.length > 0) {
|
|
17510
|
+
const fragment = getSkillPromptFragment(skillIds, allSkills, persona.id);
|
|
17511
|
+
if (fragment) {
|
|
17512
|
+
parts.push(`
|
|
17513
|
+
## Skill Rules`);
|
|
17514
|
+
parts.push(fragment);
|
|
17515
|
+
}
|
|
17516
|
+
}
|
|
17517
|
+
} catch {
|
|
17518
|
+
}
|
|
17519
|
+
return parts.join("\n");
|
|
17520
|
+
}
|
|
17521
|
+
function buildPersonaSummaries() {
|
|
17522
|
+
const personas = listPersonas();
|
|
17523
|
+
const lines = personas.map(
|
|
17524
|
+
(p) => `- **${p.name}** (${p.shortName}): ${p.description}
|
|
17525
|
+
Document types: ${p.documentTypes.join(", ")}`
|
|
17526
|
+
);
|
|
17527
|
+
return `# Available Personas
|
|
17528
|
+
|
|
17529
|
+
${lines.join("\n\n")}`;
|
|
17530
|
+
}
|
|
17531
|
+
|
|
17532
|
+
// src/mcp/persona-tools.ts
|
|
17533
|
+
import { tool as tool17 } from "@anthropic-ai/claude-agent-sdk";
|
|
17534
|
+
function createPersonaTools(ctx, marvinDir) {
|
|
17535
|
+
return [
|
|
17536
|
+
tool17(
|
|
17537
|
+
"set_persona",
|
|
17538
|
+
"Set the active persona for this session. Returns full guidance for the selected persona including behavioral rules, allowed document types, and scope. Call this before working to ensure persona-appropriate behavior.",
|
|
17539
|
+
{
|
|
17540
|
+
persona: external_exports.string().describe(
|
|
17541
|
+
'Persona ID or short name (e.g. "po", "product-owner", "dm", "delivery-manager", "tl", "tech-lead")'
|
|
17542
|
+
)
|
|
17543
|
+
},
|
|
17544
|
+
async (args) => {
|
|
17545
|
+
const resolved = ctx.setPersona(args.persona);
|
|
17546
|
+
if (!resolved) {
|
|
17547
|
+
const summaries = buildPersonaSummaries();
|
|
17548
|
+
return {
|
|
17549
|
+
content: [
|
|
17550
|
+
{
|
|
17551
|
+
type: "text",
|
|
17552
|
+
text: `Unknown persona "${args.persona}".
|
|
17553
|
+
|
|
17554
|
+
${summaries}`
|
|
17555
|
+
}
|
|
17556
|
+
],
|
|
17557
|
+
isError: true
|
|
17558
|
+
};
|
|
17559
|
+
}
|
|
17560
|
+
const guidance = buildMcpGuidance(resolved, marvinDir);
|
|
17561
|
+
return {
|
|
17562
|
+
content: [{ type: "text", text: guidance }]
|
|
17563
|
+
};
|
|
17564
|
+
}
|
|
17565
|
+
),
|
|
17566
|
+
tool17(
|
|
17567
|
+
"get_persona_guidance",
|
|
17568
|
+
"Get guidance for a persona without changing the active persona. If no persona is specified, lists all available personas with summaries.",
|
|
17569
|
+
{
|
|
17570
|
+
persona: external_exports.string().optional().describe(
|
|
17571
|
+
"Optional persona ID or short name. Omit to list all personas."
|
|
17572
|
+
)
|
|
17573
|
+
},
|
|
17574
|
+
async (args) => {
|
|
17575
|
+
if (!args.persona) {
|
|
17576
|
+
const summaries = buildPersonaSummaries();
|
|
17577
|
+
return {
|
|
17578
|
+
content: [{ type: "text", text: summaries }]
|
|
17579
|
+
};
|
|
17580
|
+
}
|
|
17581
|
+
const resolved = getPersona(args.persona);
|
|
17582
|
+
if (!resolved) {
|
|
17583
|
+
const summaries = buildPersonaSummaries();
|
|
17584
|
+
return {
|
|
17585
|
+
content: [
|
|
17586
|
+
{
|
|
17587
|
+
type: "text",
|
|
17588
|
+
text: `Unknown persona "${args.persona}".
|
|
17589
|
+
|
|
17590
|
+
${summaries}`
|
|
17591
|
+
}
|
|
17592
|
+
],
|
|
17593
|
+
isError: true
|
|
17594
|
+
};
|
|
17595
|
+
}
|
|
17596
|
+
const guidance = buildMcpGuidance(resolved, marvinDir);
|
|
17597
|
+
return {
|
|
17598
|
+
content: [{ type: "text", text: guidance }]
|
|
17599
|
+
};
|
|
17600
|
+
},
|
|
17601
|
+
{ annotations: { readOnly: true } }
|
|
17602
|
+
)
|
|
17603
|
+
];
|
|
17604
|
+
}
|
|
17605
|
+
|
|
17606
|
+
// src/mcp/tool-wrapper.ts
|
|
17607
|
+
function extractDocType(toolName) {
|
|
17608
|
+
if (toolName === "save_report") return "report";
|
|
17609
|
+
const match = toolName.match(/^(?:create|update)_(\w+)$/);
|
|
17610
|
+
return match ? match[1] : void 0;
|
|
17611
|
+
}
|
|
17612
|
+
function wrapToolsWithPersonaValidation(tools, ctx) {
|
|
17613
|
+
return tools.map((t) => {
|
|
17614
|
+
const docType = extractDocType(t.name);
|
|
17615
|
+
if (!docType) return t;
|
|
17616
|
+
return {
|
|
17617
|
+
...t,
|
|
17618
|
+
handler: async (args, extra) => {
|
|
17619
|
+
const persona = ctx.getActivePersona();
|
|
17620
|
+
if (!persona) {
|
|
17621
|
+
const summaries = buildPersonaSummaries();
|
|
17622
|
+
return {
|
|
17623
|
+
content: [
|
|
17624
|
+
{
|
|
17625
|
+
type: "text",
|
|
17626
|
+
text: `[PERSONA REQUIRED] You must set an active persona before creating or updating documents. Call the set_persona tool first.
|
|
17627
|
+
|
|
17628
|
+
${summaries}`
|
|
17629
|
+
}
|
|
17630
|
+
],
|
|
17631
|
+
isError: true
|
|
17632
|
+
};
|
|
17633
|
+
}
|
|
17634
|
+
const result = await t.handler(args, extra);
|
|
17635
|
+
if (ctx.isDocumentTypeAllowed(docType)) {
|
|
17636
|
+
return result;
|
|
17637
|
+
}
|
|
17638
|
+
const warning = [
|
|
17639
|
+
`[PERSONA WARNING] You are acting as ${persona.name} (${persona.shortName}). Creating/updating "${docType}" documents is outside your typical scope.`,
|
|
17640
|
+
"",
|
|
17641
|
+
`Your allowed document types: ${persona.documentTypes.join(", ")}`,
|
|
17642
|
+
"",
|
|
17643
|
+
"Consider whether this is the right persona for this task, or switch with set_persona.",
|
|
17644
|
+
"",
|
|
17645
|
+
"---"
|
|
17646
|
+
].join("\n");
|
|
17647
|
+
const content = Array.isArray(result.content) ? result.content.map(
|
|
17648
|
+
(block, index) => {
|
|
17649
|
+
if (index === 0 && block.type === "text" && block.text) {
|
|
17650
|
+
return { ...block, text: `${warning}
|
|
17651
|
+
${block.text}` };
|
|
17652
|
+
}
|
|
17653
|
+
return block;
|
|
17654
|
+
}
|
|
17655
|
+
) : result.content;
|
|
17656
|
+
return { ...result, content };
|
|
17657
|
+
}
|
|
17658
|
+
};
|
|
17659
|
+
});
|
|
17660
|
+
}
|
|
17661
|
+
|
|
17662
|
+
// src/mcp/stdio-server.ts
|
|
17289
17663
|
function collectTools(marvinDir) {
|
|
17290
17664
|
const config2 = loadProjectConfig(marvinDir);
|
|
17291
17665
|
const plugin = resolvePlugin(config2.methodology);
|
|
@@ -17347,8 +17721,11 @@ async function startStdioServer(options) {
|
|
|
17347
17721
|
{ name: "marvin-governance", version: "0.1.0" },
|
|
17348
17722
|
{ capabilities: { tools: {} } }
|
|
17349
17723
|
);
|
|
17350
|
-
const
|
|
17351
|
-
|
|
17724
|
+
const contextManager = new PersonaContextManager();
|
|
17725
|
+
const governanceTools = collectTools(options.marvinDir);
|
|
17726
|
+
const personaTools = createPersonaTools(contextManager, options.marvinDir);
|
|
17727
|
+
const wrappedTools = wrapToolsWithPersonaValidation(governanceTools, contextManager);
|
|
17728
|
+
registerSdkTools(server, [...personaTools, ...wrappedTools]);
|
|
17352
17729
|
const transport = new StdioServerTransport();
|
|
17353
17730
|
await server.connect(transport);
|
|
17354
17731
|
}
|
|
@@ -18064,8 +18441,8 @@ var MarvinGit = class {
|
|
|
18064
18441
|
);
|
|
18065
18442
|
}
|
|
18066
18443
|
await this.git.init();
|
|
18067
|
-
const { writeFileSync:
|
|
18068
|
-
|
|
18444
|
+
const { writeFileSync: writeFileSync9 } = await import("fs");
|
|
18445
|
+
writeFileSync9(
|
|
18069
18446
|
path13.join(this.marvinDir, ".gitignore"),
|
|
18070
18447
|
MARVIN_GITIGNORE,
|
|
18071
18448
|
"utf-8"
|
|
@@ -18375,6 +18752,7 @@ async function serveCommand() {
|
|
|
18375
18752
|
import * as fs13 from "fs";
|
|
18376
18753
|
import * as path14 from "path";
|
|
18377
18754
|
import * as YAML7 from "yaml";
|
|
18755
|
+
import matter3 from "gray-matter";
|
|
18378
18756
|
import chalk10 from "chalk";
|
|
18379
18757
|
async function skillsListCommand() {
|
|
18380
18758
|
const project = loadProject();
|
|
@@ -18388,10 +18766,12 @@ async function skillsListCommand() {
|
|
|
18388
18766
|
}
|
|
18389
18767
|
console.log(chalk10.bold("\nAvailable Skills\n"));
|
|
18390
18768
|
const idWidth = Math.max(5, ...infos.map((s) => s.id.length));
|
|
18769
|
+
const fmtWidth = Math.max(6, ...infos.map((s) => s.format.length));
|
|
18391
18770
|
const verWidth = Math.max(7, ...infos.map((s) => s.version.length));
|
|
18392
18771
|
const descWidth = Math.max(11, ...infos.map((s) => s.description.length));
|
|
18393
18772
|
const header = [
|
|
18394
18773
|
"ID".padEnd(idWidth),
|
|
18774
|
+
"Format".padEnd(fmtWidth),
|
|
18395
18775
|
"Version".padEnd(verWidth),
|
|
18396
18776
|
"Description".padEnd(descWidth),
|
|
18397
18777
|
"Personas"
|
|
@@ -18403,6 +18783,7 @@ async function skillsListCommand() {
|
|
|
18403
18783
|
console.log(
|
|
18404
18784
|
[
|
|
18405
18785
|
info.id.padEnd(idWidth),
|
|
18786
|
+
info.format.padEnd(fmtWidth),
|
|
18406
18787
|
info.version.padEnd(verWidth),
|
|
18407
18788
|
info.description.padEnd(descWidth),
|
|
18408
18789
|
personas
|
|
@@ -18471,33 +18852,82 @@ async function skillsCreateCommand(name) {
|
|
|
18471
18852
|
const project = loadProject();
|
|
18472
18853
|
const skillsDir = path14.join(project.marvinDir, "skills");
|
|
18473
18854
|
fs13.mkdirSync(skillsDir, { recursive: true });
|
|
18474
|
-
const
|
|
18475
|
-
if (fs13.existsSync(
|
|
18476
|
-
console.log(chalk10.yellow(`Skill
|
|
18855
|
+
const skillDir = path14.join(skillsDir, name);
|
|
18856
|
+
if (fs13.existsSync(skillDir)) {
|
|
18857
|
+
console.log(chalk10.yellow(`Skill directory already exists: ${skillDir}`));
|
|
18477
18858
|
return;
|
|
18478
18859
|
}
|
|
18479
|
-
|
|
18480
|
-
|
|
18481
|
-
|
|
18860
|
+
fs13.mkdirSync(skillDir, { recursive: true });
|
|
18861
|
+
const displayName = name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
18862
|
+
const frontmatter = {
|
|
18863
|
+
name,
|
|
18482
18864
|
description: `Custom skill: ${name}`,
|
|
18483
|
-
|
|
18484
|
-
|
|
18485
|
-
|
|
18486
|
-
|
|
18487
|
-
},
|
|
18488
|
-
actions: [
|
|
18489
|
-
{
|
|
18490
|
-
id: "run",
|
|
18491
|
-
name: `Run ${name}`,
|
|
18492
|
-
description: `Execute the ${name} skill`,
|
|
18493
|
-
systemPrompt: "You are a helpful assistant. Complete the requested task using the available governance tools.",
|
|
18494
|
-
maxTurns: 5
|
|
18495
|
-
}
|
|
18496
|
-
]
|
|
18865
|
+
metadata: {
|
|
18866
|
+
version: "1.0.0",
|
|
18867
|
+
personas: ["product-owner"]
|
|
18868
|
+
}
|
|
18497
18869
|
};
|
|
18498
|
-
|
|
18499
|
-
|
|
18500
|
-
|
|
18870
|
+
const body = `
|
|
18871
|
+
You have the **${displayName}** skill.
|
|
18872
|
+
`;
|
|
18873
|
+
const skillMd = matter3.stringify(body, frontmatter);
|
|
18874
|
+
fs13.writeFileSync(path14.join(skillDir, "SKILL.md"), skillMd, "utf-8");
|
|
18875
|
+
const actions = [
|
|
18876
|
+
{
|
|
18877
|
+
id: "run",
|
|
18878
|
+
name: `Run ${name}`,
|
|
18879
|
+
description: `Execute the ${name} skill`,
|
|
18880
|
+
systemPrompt: "You are a helpful assistant. Complete the requested task using the available governance tools.",
|
|
18881
|
+
maxTurns: 5
|
|
18882
|
+
}
|
|
18883
|
+
];
|
|
18884
|
+
fs13.writeFileSync(path14.join(skillDir, "actions.yaml"), YAML7.stringify(actions), "utf-8");
|
|
18885
|
+
console.log(chalk10.green(`Created skill: ${skillDir}/`));
|
|
18886
|
+
console.log(chalk10.dim(" SKILL.md \u2014 skill definition and prompt"));
|
|
18887
|
+
console.log(chalk10.dim(" actions.yaml \u2014 action definitions"));
|
|
18888
|
+
console.log(chalk10.dim("\nAdd persona-specific prompts in personas/<persona-id>.md"));
|
|
18889
|
+
}
|
|
18890
|
+
async function skillsMigrateCommand() {
|
|
18891
|
+
const project = loadProject();
|
|
18892
|
+
const skillsDir = path14.join(project.marvinDir, "skills");
|
|
18893
|
+
if (!fs13.existsSync(skillsDir)) {
|
|
18894
|
+
console.log(chalk10.dim("No skills directory found."));
|
|
18895
|
+
return;
|
|
18896
|
+
}
|
|
18897
|
+
let entries;
|
|
18898
|
+
try {
|
|
18899
|
+
entries = fs13.readdirSync(skillsDir);
|
|
18900
|
+
} catch {
|
|
18901
|
+
console.log(chalk10.red("Could not read skills directory."));
|
|
18902
|
+
return;
|
|
18903
|
+
}
|
|
18904
|
+
const yamlFiles = entries.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
18905
|
+
if (yamlFiles.length === 0) {
|
|
18906
|
+
console.log(chalk10.dim("No YAML skill files to migrate."));
|
|
18907
|
+
return;
|
|
18908
|
+
}
|
|
18909
|
+
let migrated = 0;
|
|
18910
|
+
for (const file2 of yamlFiles) {
|
|
18911
|
+
const yamlPath = path14.join(skillsDir, file2);
|
|
18912
|
+
const baseName = file2.replace(/\.(yaml|yml)$/, "");
|
|
18913
|
+
const outputDir = path14.join(skillsDir, baseName);
|
|
18914
|
+
if (fs13.existsSync(outputDir)) {
|
|
18915
|
+
console.log(chalk10.yellow(`Skipping "${file2}" \u2014 directory "${baseName}/" already exists.`));
|
|
18916
|
+
continue;
|
|
18917
|
+
}
|
|
18918
|
+
try {
|
|
18919
|
+
migrateYamlToSkillMd(yamlPath, outputDir);
|
|
18920
|
+
fs13.renameSync(yamlPath, `${yamlPath}.bak`);
|
|
18921
|
+
console.log(chalk10.green(`Migrated "${file2}" \u2192 "${baseName}/"`));
|
|
18922
|
+
migrated++;
|
|
18923
|
+
} catch (err) {
|
|
18924
|
+
console.log(chalk10.red(`Failed to migrate "${file2}": ${err}`));
|
|
18925
|
+
}
|
|
18926
|
+
}
|
|
18927
|
+
if (migrated > 0) {
|
|
18928
|
+
console.log(chalk10.dim(`
|
|
18929
|
+
${migrated} skill(s) migrated. Original files renamed to *.bak`));
|
|
18930
|
+
}
|
|
18501
18931
|
}
|
|
18502
18932
|
|
|
18503
18933
|
// src/cli/commands/import.ts
|
|
@@ -18508,12 +18938,12 @@ import chalk11 from "chalk";
|
|
|
18508
18938
|
// src/import/engine.ts
|
|
18509
18939
|
import * as fs15 from "fs";
|
|
18510
18940
|
import * as path16 from "path";
|
|
18511
|
-
import
|
|
18941
|
+
import matter5 from "gray-matter";
|
|
18512
18942
|
|
|
18513
18943
|
// src/import/classifier.ts
|
|
18514
18944
|
import * as fs14 from "fs";
|
|
18515
18945
|
import * as path15 from "path";
|
|
18516
|
-
import
|
|
18946
|
+
import matter4 from "gray-matter";
|
|
18517
18947
|
var RAW_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".pdf", ".txt"]);
|
|
18518
18948
|
var CORE_DIR_NAMES = /* @__PURE__ */ new Set(["decisions", "actions", "questions"]);
|
|
18519
18949
|
var ID_PATTERN = /^[A-Z]+-\d{3,}$/;
|
|
@@ -18539,7 +18969,7 @@ function classifyPath(inputPath, knownTypes, knownDirNames) {
|
|
|
18539
18969
|
const hasMarvinDocs = mdFiles.some((f) => {
|
|
18540
18970
|
try {
|
|
18541
18971
|
const raw = fs14.readFileSync(path15.join(resolved, f), "utf-8");
|
|
18542
|
-
const { data } =
|
|
18972
|
+
const { data } = matter4(raw);
|
|
18543
18973
|
return isValidMarvinDocument(data, knownTypes);
|
|
18544
18974
|
} catch {
|
|
18545
18975
|
return false;
|
|
@@ -18560,7 +18990,7 @@ function classifyFile(filePath, knownTypes) {
|
|
|
18560
18990
|
if (ext === ".md") {
|
|
18561
18991
|
try {
|
|
18562
18992
|
const raw = fs14.readFileSync(resolved, "utf-8");
|
|
18563
|
-
const { data } =
|
|
18993
|
+
const { data } = matter4(raw);
|
|
18564
18994
|
if (isValidMarvinDocument(data, knownTypes)) {
|
|
18565
18995
|
return { type: "marvin-document", inputPath: resolved };
|
|
18566
18996
|
}
|
|
@@ -18771,7 +19201,7 @@ function collectMarvinDocs(dir, knownTypes) {
|
|
|
18771
19201
|
const filePath = path16.join(dir, file2);
|
|
18772
19202
|
try {
|
|
18773
19203
|
const raw = fs15.readFileSync(filePath, "utf-8");
|
|
18774
|
-
const { data, content } =
|
|
19204
|
+
const { data, content } = matter5(raw);
|
|
18775
19205
|
if (isValidMarvinDocument(data, knownTypes)) {
|
|
18776
19206
|
docs.push({
|
|
18777
19207
|
frontmatter: data,
|
|
@@ -18865,7 +19295,7 @@ function planFromSingleDocument(classification, store, _marvinDir, options) {
|
|
|
18865
19295
|
const filePath = classification.inputPath;
|
|
18866
19296
|
const knownTypes = store.registeredTypes;
|
|
18867
19297
|
const raw = fs15.readFileSync(filePath, "utf-8");
|
|
18868
|
-
const { data, content } =
|
|
19298
|
+
const { data, content } = matter5(raw);
|
|
18869
19299
|
if (!isValidMarvinDocument(data, knownTypes)) {
|
|
18870
19300
|
return [];
|
|
18871
19301
|
}
|
|
@@ -19262,7 +19692,7 @@ function createProgram() {
|
|
|
19262
19692
|
const program = new Command();
|
|
19263
19693
|
program.name("marvin").description(
|
|
19264
19694
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
19265
|
-
).version("0.
|
|
19695
|
+
).version("0.2.0");
|
|
19266
19696
|
program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
19267
19697
|
await initCommand();
|
|
19268
19698
|
});
|
|
@@ -19323,9 +19753,12 @@ function createProgram() {
|
|
|
19323
19753
|
skillsCmd.command("remove <skill>").description("Unassign a skill from a persona").requiredOption("--as <persona>", "Persona to remove the skill from").action(async (skill, options) => {
|
|
19324
19754
|
await skillsRemoveCommand(skill, options);
|
|
19325
19755
|
});
|
|
19326
|
-
skillsCmd.command("create <name>").description("Create a
|
|
19756
|
+
skillsCmd.command("create <name>").description("Create a new skill in .marvin/skills/ (SKILL.md format)").action(async (name) => {
|
|
19327
19757
|
await skillsCreateCommand(name);
|
|
19328
19758
|
});
|
|
19759
|
+
skillsCmd.command("migrate").description("Migrate YAML skill files to SKILL.md directory format").action(async () => {
|
|
19760
|
+
await skillsMigrateCommand();
|
|
19761
|
+
});
|
|
19329
19762
|
return program;
|
|
19330
19763
|
}
|
|
19331
19764
|
export {
|
|
@@ -19348,13 +19781,16 @@ export {
|
|
|
19348
19781
|
getPersona,
|
|
19349
19782
|
getPluginPromptFragment,
|
|
19350
19783
|
getPluginTools,
|
|
19784
|
+
getSkillAgentDefinitions,
|
|
19351
19785
|
getSkillPromptFragment,
|
|
19352
19786
|
getSkillTools,
|
|
19353
19787
|
isMarvinProject,
|
|
19354
19788
|
listPersonas,
|
|
19355
19789
|
loadAllSkills,
|
|
19356
19790
|
loadProject,
|
|
19791
|
+
loadSkillFromDirectory,
|
|
19357
19792
|
loadUserConfig,
|
|
19793
|
+
migrateYamlToSkillMd,
|
|
19358
19794
|
parseDocument,
|
|
19359
19795
|
registerSdkTools,
|
|
19360
19796
|
resolvePersonaId,
|