Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/agent/mcp-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { createSessionTools } from "./tools/sessions.js";
import { createWebTools } from "./tools/web.js";
import { createDoctorTools } from "./tools/doctor.js";
import { createConceptTools } from "../methodology/tools.js";
import { createBootstrapTools } from "../methodology/bootstrap-tools.js";
import type { NavGroup } from "../web/templates/layout.js";

export interface McpServerOptions {
Expand Down Expand Up @@ -52,6 +53,10 @@ export function createMarvinMcpServer(
marvinDir: options?.marvinDir,
}),
...createConceptTools(options?.config),
...createBootstrapTools(store, {
config: options?.config,
manifest: options?.manifest,
}),
];

return createSdkMcpServer({
Expand Down
21 changes: 16 additions & 5 deletions src/doctor/health/onboarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export function buildOnboardingGuide(ctx: HealthContext): OnboardingGuide {
const pendingSources = ctx.manifest?.list("pending")?.length ?? 0;
const hasActions = (counts["action"] ?? 0) > 0;
const hasEpics = (counts["epic"] ?? 0) > 0;
const hasSprints = (counts["sprint"] ?? 0) > 0;
const hasFeatures = (counts["feature"] ?? 0) > 0;
const hasUseCases = (counts["use-case"] ?? 0) > 0;
const hasJira = !!ctx.config.jira?.projectKey?.trim();
Expand Down Expand Up @@ -105,13 +104,25 @@ export function buildOnboardingGuide(ctx: HealthContext): OnboardingGuide {
});

// Step 6: Set up Sprint 0
const hasWorkItems = hasActions || hasFeatures || hasUseCases;
const hasSprintZero = ctx.store.list({ type: "sprint" }).some((s) => {
const tags: string[] = s.frontmatter.tags ?? [];
return (
tags.includes("sprint-0") ||
tags.includes("bootstrapping") ||
s.frontmatter.title?.toLowerCase().includes("sprint 0")
);
});
steps.push({
order: order++,
title: "Set up Sprint 0",
description:
"As DM, create a Sprint 0 to organize bootstrapping work: infrastructure provisioning, CI/CD setup, backlog refinement, and ceremony scheduling. Sprint 0 is not a regular sprint — it's a variable-duration bootstrapping phase that ensures the team is ready for Sprint 1.",
tool: "create_sprint",
done: hasSprints,
description: hasSprintZero
? "Sprint 0 has been created."
: hasWorkItems
? "As DM, run bootstrap_sprint_zero to create a guided Sprint 0 with linked bootstrapping actions for infrastructure, backlog refinement, ceremonies, and integrations. This generates a fully scaffolded sprint with checklist items pre-populated."
: "As DM, create a Sprint 0 to organize bootstrapping work: infrastructure provisioning, CI/CD setup, backlog refinement, and ceremony scheduling. Sprint 0 is not a regular sprint — it's a variable-duration bootstrapping phase that ensures the team is ready for Sprint 1.",
tool: hasWorkItems && !hasSprintZero ? "bootstrap_sprint_zero" : "create_sprint",
done: hasSprintZero,
});

// Step 7: Configure Jira integration
Expand Down
92 changes: 92 additions & 0 deletions src/methodology/bootstrap-tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { z } from "zod/v4";
import { tool, type SdkMcpToolDefinition } from "@anthropic-ai/claude-agent-sdk";
import type { DocumentStore } from "../storage/store.js";
import type { MarvinProjectConfig } from "../core/config.js";
import type { SourceManifestManager } from "../sources/manifest.js";
import { runStep, type BootstrapStep, type BootstrapSection } from "./bootstrap.js";

export interface BootstrapToolOptions {
config?: MarvinProjectConfig;
manifest?: SourceManifestManager;
}

export function createBootstrapTools(
store: DocumentStore,
options?: BootstrapToolOptions,
): SdkMcpToolDefinition<any>[] {
Comment on lines +13 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify explicit `any` usage in this file
rg -nP --type=ts 'SdkMcpToolDefinition<any>|:\s*any\b' src/methodology/bootstrap-tools.ts

Repository: ablanrob/marvin-cli

Length of output: 99


🏁 Script executed:

cat -n src/methodology/bootstrap-tools.ts | head -100

Repository: ablanrob/marvin-cli

Length of output: 3557


🏁 Script executed:

# Check for type definitions of BootstrapStep, BootstrapSection
rg -n "type|interface.*\b(BootstrapStep|BootstrapSection|BootstrapToolOptions)\b" src/methodology/bootstrap-tools.ts

Repository: ablanrob/marvin-cli

Length of output: 597


🏁 Script executed:

# Check for any other usages of 'any' in the file
rg -n ":\s*any\b|<any>" src/methodology/bootstrap-tools.ts

Repository: ablanrob/marvin-cli

Length of output: 99


Replace any with explicit BootstrapToolArgs type.

The return type SdkMcpToolDefinition<any>[] at line 16 violates strict typing requirements. Since BootstrapStep and BootstrapSection are already imported from ./bootstrap.js, define an explicit args interface and use it:

Suggested fix
+interface BootstrapToolArgs {
+  step?: BootstrapStep;
+  section?: BootstrapSection;
+  includeAemAddendum?: boolean;
+}
+
 export function createBootstrapTools(
   store: DocumentStore,
   options?: BootstrapToolOptions,
-): SdkMcpToolDefinition<any>[] {
+): SdkMcpToolDefinition<BootstrapToolArgs>[] {

Per coding guidelines: "Never use any unless absolutely unavoidable — prefer explicit types." This improves IDE support and maintainability without runtime cost.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/methodology/bootstrap-tools.ts` around lines 13 - 16, The return type
currently uses SdkMcpToolDefinition<any>[]; define a concrete args interface
(e.g., BootstrapToolArgs) that models the expected properties passed to
bootstrap tools (leveraging existing types like BootstrapStep and
BootstrapSection from ./bootstrap.js) and replace the any with
SdkMcpToolDefinition<BootstrapToolArgs>[] on createBootstrapTools; ensure the
new interface is exported/declared near the top of
src/methodology/bootstrap-tools.ts and update any internal uses or type
annotations inside createBootstrapTools to use BootstrapToolArgs for strict
typing.

return [
tool(
"bootstrap_sprint_zero",
"Guided multi-step workflow that produces a draft Sprint 0 with linked bootstrapping actions. Call with no arguments to start at survey step. Each step returns next_step to chain calls. Only the commit step writes to disk; all other steps are read-only.",
{
step: z
.enum(["survey", "draft", "populate", "review", "commit"])
.optional()
.describe("Workflow step. Omit on first call to start at survey."),
section: z
.enum([
"infrastructure-provisioning",
"backlog-refinement",
"ceremony-scheduling",
"integration-setup",
"aem-addendum",
])
.optional()
.describe("Restrict populate step to one section."),
includeAemAddendum: z
.boolean()
.optional()
.describe(
"Override AEM addendum inclusion. Default: auto-detect from methodology config.",
),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
},
async (args) => {
if (!options?.config) {
return {
content: [
{
type: "text" as const,
text: "Bootstrap unavailable: project config not initialized.",
},
],
isError: true,
};
}

const ctx = {
store,
config: options.config,
manifest: options.manifest,
includeAemAddendum: args.includeAemAddendum,
};

try {
const result = runStep(
ctx,
args.step as BootstrapStep | undefined,
args.section as BootstrapSection | undefined,
);

return {
content: [
{
type: "text" as const,
text: JSON.stringify(result, null, 2),
},
],
};
} catch (err) {
return {
content: [
{
type: "text" as const,
text: `Bootstrap error: ${err instanceof Error ? err.message : String(err)}`,
},
],
isError: true,
};
}
},
),
];
}
Loading
Loading