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.js CHANGED
@@ -2129,10 +2129,10 @@ var nanoid = /^[a-zA-Z0-9_-]{21}$/;
2129
2129
  var duration = /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/;
2130
2130
  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)?)??$/;
2131
2131
  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})$/;
2132
- var uuid = (version3) => {
2133
- if (!version3)
2132
+ var uuid = (version2) => {
2133
+ if (!version2)
2134
2134
  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)$/;
2135
- return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version3}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
2135
+ 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})$`);
2136
2136
  };
2137
2137
  var uuid4 = /* @__PURE__ */ uuid(4);
2138
2138
  var uuid6 = /* @__PURE__ */ uuid(6);
@@ -14288,10 +14288,10 @@ function fromJSONSchema(schema, params) {
14288
14288
  if (typeof schema === "boolean") {
14289
14289
  return schema ? z.any() : z.never();
14290
14290
  }
14291
- const version3 = detectVersion(schema, params?.defaultTarget);
14291
+ const version2 = detectVersion(schema, params?.defaultTarget);
14292
14292
  const defs = schema.$defs || schema.definitions || {};
14293
14293
  const ctx = {
14294
- version: version3,
14294
+ version: version2,
14295
14295
  defs,
14296
14296
  refs: /* @__PURE__ */ new Map(),
14297
14297
  processing: /* @__PURE__ */ new Set(),
@@ -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 query3
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 file2 of entries) {
16923
- if (!file2.endsWith(".yaml") && !file2.endsWith(".yml")) continue;
17012
+ for (const entry of entries) {
17013
+ const entryPath = path7.join(skillsDir, entry);
17014
+ try {
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;
16924
17024
  try {
16925
- const raw = fs7.readFileSync(path7.join(skillsDir, file2), "utf-8");
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
- // src/skills/action-tools.ts
17002
- import { tool as tool16 } from "@anthropic-ai/claude-agent-sdk";
17003
-
17004
- // src/skills/action-runner.ts
17005
- import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
17006
- var GOVERNANCE_TOOL_NAMES = [
17007
- "mcp__marvin-governance__list_decisions",
17008
- "mcp__marvin-governance__get_decision",
17009
- "mcp__marvin-governance__create_decision",
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
- allowedTools,
17034
- cwd: context.projectRoot,
17035
- tools: [],
17036
- permissionMode: "acceptEdits"
17037
- }
17038
- });
17039
- const textParts = [];
17040
- for await (const message of conversation) {
17041
- if (message.type === "assistant") {
17042
- const textBlocks = message.message.content.filter(
17043
- (b) => b.type === "text"
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
- // src/skills/action-tools.ts
17062
- function createSkillActionTools(skills, context) {
17063
- const tools = [];
17064
- for (const skill of skills) {
17065
- if (!skill.actions) continue;
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 skillsWithActions = skillIds.map((id) => allSkills.get(id)).filter((s) => s.actions && s.actions.length > 0);
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: [...codeSkillTools, ...actionTools]
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 = query3({
17286
+ const conversation = query2({
17203
17287
  prompt,
17204
17288
  options: queryOptions
17205
17289
  });
@@ -17286,6 +17370,89 @@ 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/stdio-server.ts
17289
17456
  function collectTools(marvinDir) {
17290
17457
  const config2 = loadProjectConfig(marvinDir);
17291
17458
  const plugin = resolvePlugin(config2.methodology);
@@ -17354,7 +17521,6 @@ async function startStdioServer(options) {
17354
17521
  }
17355
17522
 
17356
17523
  // src/cli/program.ts
17357
- import { createRequire } from "module";
17358
17524
  import { Command } from "commander";
17359
17525
 
17360
17526
  // src/cli/commands/init.ts
@@ -18065,8 +18231,8 @@ var MarvinGit = class {
18065
18231
  );
18066
18232
  }
18067
18233
  await this.git.init();
18068
- const { writeFileSync: writeFileSync8 } = await import("fs");
18069
- writeFileSync8(
18234
+ const { writeFileSync: writeFileSync9 } = await import("fs");
18235
+ writeFileSync9(
18070
18236
  path13.join(this.marvinDir, ".gitignore"),
18071
18237
  MARVIN_GITIGNORE,
18072
18238
  "utf-8"
@@ -18376,6 +18542,7 @@ async function serveCommand() {
18376
18542
  import * as fs13 from "fs";
18377
18543
  import * as path14 from "path";
18378
18544
  import * as YAML7 from "yaml";
18545
+ import matter3 from "gray-matter";
18379
18546
  import chalk10 from "chalk";
18380
18547
  async function skillsListCommand() {
18381
18548
  const project = loadProject();
@@ -18389,10 +18556,12 @@ async function skillsListCommand() {
18389
18556
  }
18390
18557
  console.log(chalk10.bold("\nAvailable Skills\n"));
18391
18558
  const idWidth = Math.max(5, ...infos.map((s) => s.id.length));
18559
+ const fmtWidth = Math.max(6, ...infos.map((s) => s.format.length));
18392
18560
  const verWidth = Math.max(7, ...infos.map((s) => s.version.length));
18393
18561
  const descWidth = Math.max(11, ...infos.map((s) => s.description.length));
18394
18562
  const header = [
18395
18563
  "ID".padEnd(idWidth),
18564
+ "Format".padEnd(fmtWidth),
18396
18565
  "Version".padEnd(verWidth),
18397
18566
  "Description".padEnd(descWidth),
18398
18567
  "Personas"
@@ -18404,6 +18573,7 @@ async function skillsListCommand() {
18404
18573
  console.log(
18405
18574
  [
18406
18575
  info.id.padEnd(idWidth),
18576
+ info.format.padEnd(fmtWidth),
18407
18577
  info.version.padEnd(verWidth),
18408
18578
  info.description.padEnd(descWidth),
18409
18579
  personas
@@ -18472,33 +18642,82 @@ async function skillsCreateCommand(name) {
18472
18642
  const project = loadProject();
18473
18643
  const skillsDir = path14.join(project.marvinDir, "skills");
18474
18644
  fs13.mkdirSync(skillsDir, { recursive: true });
18475
- const filePath = path14.join(skillsDir, `${name}.yaml`);
18476
- if (fs13.existsSync(filePath)) {
18477
- console.log(chalk10.yellow(`Skill file already exists: ${filePath}`));
18645
+ const skillDir = path14.join(skillsDir, name);
18646
+ if (fs13.existsSync(skillDir)) {
18647
+ console.log(chalk10.yellow(`Skill directory already exists: ${skillDir}`));
18478
18648
  return;
18479
18649
  }
18480
- const template = {
18481
- id: name,
18482
- name: name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
18650
+ fs13.mkdirSync(skillDir, { recursive: true });
18651
+ const displayName = name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
18652
+ const frontmatter = {
18653
+ name,
18483
18654
  description: `Custom skill: ${name}`,
18484
- version: "1.0.0",
18485
- personas: ["product-owner"],
18486
- promptFragments: {
18487
- "*": `You have the **${name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())}** skill.`
18488
- },
18489
- actions: [
18490
- {
18491
- id: "run",
18492
- name: `Run ${name}`,
18493
- description: `Execute the ${name} skill`,
18494
- systemPrompt: "You are a helpful assistant. Complete the requested task using the available governance tools.",
18495
- maxTurns: 5
18496
- }
18497
- ]
18655
+ metadata: {
18656
+ version: "1.0.0",
18657
+ personas: ["product-owner"]
18658
+ }
18498
18659
  };
18499
- fs13.writeFileSync(filePath, YAML7.stringify(template), "utf-8");
18500
- console.log(chalk10.green(`Created skill template: ${filePath}`));
18501
- console.log(chalk10.dim("Edit the YAML file to customize your skill."));
18660
+ const body = `
18661
+ You have the **${displayName}** skill.
18662
+ `;
18663
+ const skillMd = matter3.stringify(body, frontmatter);
18664
+ fs13.writeFileSync(path14.join(skillDir, "SKILL.md"), skillMd, "utf-8");
18665
+ const actions = [
18666
+ {
18667
+ id: "run",
18668
+ name: `Run ${name}`,
18669
+ description: `Execute the ${name} skill`,
18670
+ systemPrompt: "You are a helpful assistant. Complete the requested task using the available governance tools.",
18671
+ maxTurns: 5
18672
+ }
18673
+ ];
18674
+ fs13.writeFileSync(path14.join(skillDir, "actions.yaml"), YAML7.stringify(actions), "utf-8");
18675
+ console.log(chalk10.green(`Created skill: ${skillDir}/`));
18676
+ console.log(chalk10.dim(" SKILL.md \u2014 skill definition and prompt"));
18677
+ console.log(chalk10.dim(" actions.yaml \u2014 action definitions"));
18678
+ console.log(chalk10.dim("\nAdd persona-specific prompts in personas/<persona-id>.md"));
18679
+ }
18680
+ async function skillsMigrateCommand() {
18681
+ const project = loadProject();
18682
+ const skillsDir = path14.join(project.marvinDir, "skills");
18683
+ if (!fs13.existsSync(skillsDir)) {
18684
+ console.log(chalk10.dim("No skills directory found."));
18685
+ return;
18686
+ }
18687
+ let entries;
18688
+ try {
18689
+ entries = fs13.readdirSync(skillsDir);
18690
+ } catch {
18691
+ console.log(chalk10.red("Could not read skills directory."));
18692
+ return;
18693
+ }
18694
+ const yamlFiles = entries.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
18695
+ if (yamlFiles.length === 0) {
18696
+ console.log(chalk10.dim("No YAML skill files to migrate."));
18697
+ return;
18698
+ }
18699
+ let migrated = 0;
18700
+ for (const file2 of yamlFiles) {
18701
+ const yamlPath = path14.join(skillsDir, file2);
18702
+ const baseName = file2.replace(/\.(yaml|yml)$/, "");
18703
+ const outputDir = path14.join(skillsDir, baseName);
18704
+ if (fs13.existsSync(outputDir)) {
18705
+ console.log(chalk10.yellow(`Skipping "${file2}" \u2014 directory "${baseName}/" already exists.`));
18706
+ continue;
18707
+ }
18708
+ try {
18709
+ migrateYamlToSkillMd(yamlPath, outputDir);
18710
+ fs13.renameSync(yamlPath, `${yamlPath}.bak`);
18711
+ console.log(chalk10.green(`Migrated "${file2}" \u2192 "${baseName}/"`));
18712
+ migrated++;
18713
+ } catch (err) {
18714
+ console.log(chalk10.red(`Failed to migrate "${file2}": ${err}`));
18715
+ }
18716
+ }
18717
+ if (migrated > 0) {
18718
+ console.log(chalk10.dim(`
18719
+ ${migrated} skill(s) migrated. Original files renamed to *.bak`));
18720
+ }
18502
18721
  }
18503
18722
 
18504
18723
  // src/cli/commands/import.ts
@@ -18509,12 +18728,12 @@ import chalk11 from "chalk";
18509
18728
  // src/import/engine.ts
18510
18729
  import * as fs15 from "fs";
18511
18730
  import * as path16 from "path";
18512
- import matter3 from "gray-matter";
18731
+ import matter5 from "gray-matter";
18513
18732
 
18514
18733
  // src/import/classifier.ts
18515
18734
  import * as fs14 from "fs";
18516
18735
  import * as path15 from "path";
18517
- import matter2 from "gray-matter";
18736
+ import matter4 from "gray-matter";
18518
18737
  var RAW_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".pdf", ".txt"]);
18519
18738
  var CORE_DIR_NAMES = /* @__PURE__ */ new Set(["decisions", "actions", "questions"]);
18520
18739
  var ID_PATTERN = /^[A-Z]+-\d{3,}$/;
@@ -18540,7 +18759,7 @@ function classifyPath(inputPath, knownTypes, knownDirNames) {
18540
18759
  const hasMarvinDocs = mdFiles.some((f) => {
18541
18760
  try {
18542
18761
  const raw = fs14.readFileSync(path15.join(resolved, f), "utf-8");
18543
- const { data } = matter2(raw);
18762
+ const { data } = matter4(raw);
18544
18763
  return isValidMarvinDocument(data, knownTypes);
18545
18764
  } catch {
18546
18765
  return false;
@@ -18561,7 +18780,7 @@ function classifyFile(filePath, knownTypes) {
18561
18780
  if (ext === ".md") {
18562
18781
  try {
18563
18782
  const raw = fs14.readFileSync(resolved, "utf-8");
18564
- const { data } = matter2(raw);
18783
+ const { data } = matter4(raw);
18565
18784
  if (isValidMarvinDocument(data, knownTypes)) {
18566
18785
  return { type: "marvin-document", inputPath: resolved };
18567
18786
  }
@@ -18772,7 +18991,7 @@ function collectMarvinDocs(dir, knownTypes) {
18772
18991
  const filePath = path16.join(dir, file2);
18773
18992
  try {
18774
18993
  const raw = fs15.readFileSync(filePath, "utf-8");
18775
- const { data, content } = matter3(raw);
18994
+ const { data, content } = matter5(raw);
18776
18995
  if (isValidMarvinDocument(data, knownTypes)) {
18777
18996
  docs.push({
18778
18997
  frontmatter: data,
@@ -18866,7 +19085,7 @@ function planFromSingleDocument(classification, store, _marvinDir, options) {
18866
19085
  const filePath = classification.inputPath;
18867
19086
  const knownTypes = store.registeredTypes;
18868
19087
  const raw = fs15.readFileSync(filePath, "utf-8");
18869
- const { data, content } = matter3(raw);
19088
+ const { data, content } = matter5(raw);
18870
19089
  if (!isValidMarvinDocument(data, knownTypes)) {
18871
19090
  return [];
18872
19091
  }
@@ -19259,13 +19478,11 @@ Analyzing meeting: ${meetingDoc.frontmatter.title}`));
19259
19478
  }
19260
19479
 
19261
19480
  // src/cli/program.ts
19262
- var require2 = createRequire(import.meta.url);
19263
- var { version: version2 } = require2("../../package.json");
19264
19481
  function createProgram() {
19265
19482
  const program = new Command();
19266
19483
  program.name("marvin").description(
19267
19484
  "AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
19268
- ).version(version2);
19485
+ ).version("0.2.0");
19269
19486
  program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
19270
19487
  await initCommand();
19271
19488
  });
@@ -19326,9 +19543,12 @@ function createProgram() {
19326
19543
  skillsCmd.command("remove <skill>").description("Unassign a skill from a persona").requiredOption("--as <persona>", "Persona to remove the skill from").action(async (skill, options) => {
19327
19544
  await skillsRemoveCommand(skill, options);
19328
19545
  });
19329
- skillsCmd.command("create <name>").description("Create a YAML skill template in .marvin/skills/").action(async (name) => {
19546
+ skillsCmd.command("create <name>").description("Create a new skill in .marvin/skills/ (SKILL.md format)").action(async (name) => {
19330
19547
  await skillsCreateCommand(name);
19331
19548
  });
19549
+ skillsCmd.command("migrate").description("Migrate YAML skill files to SKILL.md directory format").action(async () => {
19550
+ await skillsMigrateCommand();
19551
+ });
19332
19552
  return program;
19333
19553
  }
19334
19554
  export {
@@ -19351,13 +19571,16 @@ export {
19351
19571
  getPersona,
19352
19572
  getPluginPromptFragment,
19353
19573
  getPluginTools,
19574
+ getSkillAgentDefinitions,
19354
19575
  getSkillPromptFragment,
19355
19576
  getSkillTools,
19356
19577
  isMarvinProject,
19357
19578
  listPersonas,
19358
19579
  loadAllSkills,
19359
19580
  loadProject,
19581
+ loadSkillFromDirectory,
19360
19582
  loadUserConfig,
19583
+ migrateYamlToSkillMd,
19361
19584
  parseDocument,
19362
19585
  registerSdkTools,
19363
19586
  resolvePersonaId,