skills/atemndobs/nebula-rfp/proposal-builder

proposal-builder

SKILL.md

Proposal Builder Skill

Overview

This skill manages reusable proposal templates, capability blocks, and content library for rapid RFP response assembly.

Content Library Structure

interface ContentLibrary {
  templates: ProposalTemplate[];
  capabilityBlocks: CapabilityBlock[];
  caseStudies: CaseStudy[];
  teamBios: TeamBio[];
  boilerplate: BoilerplateSection[];
}

interface CapabilityBlock {
  id: string;
  name: string;
  category: string;
  content: string;
  keywords: string[];
  lastUpdated: Date;
}

interface CaseStudy {
  id: string;
  title: string;
  client: string;
  industry: string;
  challenge: string;
  approach: string;
  results: string;
  technologies: string[];
  timeline: string;
  teamSize: number;
}

interface TeamBio {
  id: string;
  name: string;
  role: string;
  summary: string;
  expertise: string[];
  certifications: string[];
  yearsExperience: number;
}

Default Capability Blocks

const DEFAULT_BLOCKS: CapabilityBlock[] = [
  {
    id: "serverless",
    name: "Serverless Architecture",
    category: "technical",
    keywords: ["serverless", "lambda", "aws", "cloud-native"],
    content: `Our serverless-first approach leverages AWS Lambda, API Gateway, and managed services:

- **Zero infrastructure management**: Focus on business logic
- **Automatic scaling**: Handle traffic spikes seamlessly
- **Pay-per-use pricing**: Cost-efficient compute
- **Built-in high availability**: Multi-AZ by default

We've delivered 20+ serverless production systems for government and enterprise clients.`,
  },
  {
    id: "apis",
    name: "API Development & Integration",
    category: "technical",
    keywords: ["api", "rest", "graphql", "integration"],
    content: `We design and implement modern APIs for seamless integration:

- **RESTful & GraphQL APIs**: Right pattern for each use case
- **OpenAPI documentation**: Complete API contracts
- **OAuth 2.0 & JWT**: Secure authentication
- **Rate limiting & monitoring**: Production-ready from day one

Our APIs serve millions of requests daily across multiple clients.`,
  },
  {
    id: "devsecops",
    name: "DevSecOps & CI/CD",
    category: "technical",
    keywords: ["devops", "ci/cd", "security", "automation"],
    content: `Security-integrated DevOps practices for reliable delivery:

- **Infrastructure as Code**: Terraform, CloudFormation, CDK
- **CI/CD pipelines**: GitHub Actions, GitLab CI, AWS CodePipeline
- **Security scanning**: SAST, DAST, dependency scanning
- **Compliance automation**: FedRAMP, SOC 2 support`,
  },
  {
    id: "cloud_migration",
    name: "Cloud Migration",
    category: "technical",
    keywords: ["migration", "cloud", "modernization"],
    content: `Proven migration methodology minimizes risk:

**Assessment**: Application portfolio analysis, dependency mapping
**Strategy**: 6 Rs evaluation (Rehost, Replatform, Refactor, etc.)
**Execution**: Phased migration with rollback plans
**Optimization**: FinOps practices, performance tuning

Successfully migrated 50+ applications, reducing costs 30-50%.`,
  },
];

Proposal Templates

Formal RFP Template Sections

Section Purpose Content Type
Cover Letter Personalized introduction Custom
Executive Summary Value proposition Custom + boilerplate
Technical Approach Solution architecture Capability blocks
Project Plan Timeline & milestones Template
Team & Staffing Organization & bios Team bios
Past Performance Case studies Case studies
Security & Compliance Standards met Boilerplate
Pricing Cost breakdown Custom

Template Placeholders

{{CLIENT_NAME}}        - Agency or organization name
{{PROJECT_TITLE}}      - RFP title or project name
{{SUBMISSION_DATE}}    - Proposal submission date
{{VALUE_PROPOSITION}}  - Customized value statement
{{TECHNICAL_APPROACH}} - Assembled capability blocks
{{TEAM_BIOS}}          - Selected team member bios
{{CASE_STUDIES}}       - Selected relevant case studies
{{TIMELINE}}           - Project timeline
{{PRICING_TABLE}}      - Cost breakdown

Convex Implementation

Content Management

// convex/templates.ts
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";

export const listCapabilityBlocks = query({
  args: { category: v.optional(v.string()) },
  handler: async (ctx, args) => {
    let q = ctx.db.query("capabilityBlocks");
    if (args.category) {
      q = q.filter((q) => q.eq(q.field("category"), args.category));
    }
    return await q.collect();
  },
});

export const matchBlocksToRfp = query({
  args: { rfpId: v.id("rfps") },
  handler: async (ctx, args) => {
    const rfp = await ctx.db.get(args.rfpId);
    if (!rfp) return [];

    const blocks = await ctx.db.query("capabilityBlocks").collect();
    const text = `${rfp.title} ${rfp.description}`.toLowerCase();

    // Score blocks by keyword matches
    const scored = blocks.map((block) => {
      const matches = block.keywords.filter((kw) =>
        text.includes(kw.toLowerCase())
      );
      return {
        ...block,
        matchScore: matches.length,
        matchedKeywords: matches,
      };
    });

    return scored
      .filter((b) => b.matchScore > 0)
      .sort((a, b) => b.matchScore - a.matchScore);
  },
});

export const createCapabilityBlock = mutation({
  args: {
    name: v.string(),
    category: v.string(),
    content: v.string(),
    keywords: v.array(v.string()),
  },
  handler: async (ctx, args) => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) throw new Error("Not authenticated");

    return await ctx.db.insert("capabilityBlocks", {
      ...args,
      lastUpdated: Date.now(),
    });
  },
});

Proposal Assembly

// convex/proposals.ts
import { action } from "./_generated/server";
import { v } from "convex/values";
import { internal } from "./_generated/api";

export const assembleProposal = action({
  args: {
    rfpId: v.id("rfps"),
    templateId: v.string(),
    selectedBlocks: v.array(v.string()),
    selectedCaseStudies: v.array(v.string()),
    selectedTeam: v.array(v.string()),
  },
  handler: async (ctx, args) => {
    // Fetch RFP
    const rfp = await ctx.runQuery(internal.rfps.get, { id: args.rfpId });
    if (!rfp) throw new Error("RFP not found");

    // Fetch template
    const template = await ctx.runQuery(internal.templates.getTemplate, {
      id: args.templateId,
    });

    // Fetch selected content
    const blocks = await ctx.runQuery(internal.templates.getBlocksByIds, {
      ids: args.selectedBlocks,
    });
    const caseStudies = await ctx.runQuery(internal.templates.getCaseStudiesByIds, {
      ids: args.selectedCaseStudies,
    });
    const team = await ctx.runQuery(internal.templates.getTeamBiosByIds, {
      ids: args.selectedTeam,
    });

    // Assemble proposal
    let content = template.content;

    // Replace placeholders
    content = content
      .replace(/\{\{CLIENT_NAME\}\}/g, extractClientName(rfp))
      .replace(/\{\{PROJECT_TITLE\}\}/g, rfp.title)
      .replace(/\{\{SUBMISSION_DATE\}\}/g, formatDate(new Date()));

    // Insert capability blocks
    const techSection = blocks.map((b) => `### ${b.name}\n\n${b.content}`).join("\n\n");
    content = content.replace(/\{\{TECHNICAL_APPROACH\}\}/g, techSection);

    // Insert case studies
    const caseSection = caseStudies.map(formatCaseStudy).join("\n\n---\n\n");
    content = content.replace(/\{\{CASE_STUDIES\}\}/g, caseSection);

    // Insert team bios
    const teamSection = team.map(formatTeamBio).join("\n\n");
    content = content.replace(/\{\{TEAM_BIOS\}\}/g, teamSection);

    // Save draft
    await ctx.runMutation(internal.pursuits.saveDraft, {
      rfpId: args.rfpId,
      content,
    });

    return { content, wordCount: countWords(content) };
  },
});

function formatCaseStudy(cs: CaseStudy): string {
  return `### ${cs.title}

**Client:** ${cs.client}
**Industry:** ${cs.industry}

**Challenge:** ${cs.challenge}

**Approach:** ${cs.approach}

**Results:**
${cs.results}

**Technologies:** ${cs.technologies.join(", ")}
**Timeline:** ${cs.timeline}
**Team Size:** ${cs.teamSize}`;
}

function formatTeamBio(bio: TeamBio): string {
  return `#### ${bio.name} - ${bio.role}

${bio.summary}

**Expertise:** ${bio.expertise.join(", ")}
**Certifications:** ${bio.certifications.join(", ")}
**Experience:** ${bio.yearsExperience}+ years`;
}

UI Components

// components/ProposalBuilder.tsx
export function ProposalBuilder({ rfpId }: { rfpId: Id<"rfps"> }) {
  const [selectedTemplate, setSelectedTemplate] = useState<string>();
  const [selectedBlocks, setSelectedBlocks] = useState<string[]>([]);
  const [selectedStudies, setSelectedStudies] = useState<string[]>([]);
  const [selectedTeam, setSelectedTeam] = useState<string[]>([]);

  const templates = useQuery(api.templates.list);
  const matchedBlocks = useQuery(api.templates.matchBlocksToRfp, { rfpId });
  const caseStudies = useQuery(api.templates.listCaseStudies);
  const team = useQuery(api.templates.listTeamBios);

  const assemble = useMutation(api.proposals.assembleProposal);

  const handleAssemble = async () => {
    await assemble({
      rfpId,
      templateId: selectedTemplate!,
      selectedBlocks,
      selectedCaseStudies: selectedStudies,
      selectedTeam,
    });
  };

  return (
    <div className="grid grid-cols-3 gap-6 p-6">
      {/* Template Selection */}
      <section className="space-y-4">
        <h3 className="font-semibold">1. Select Template</h3>
        <TemplateSelector
          templates={templates ?? []}
          selected={selectedTemplate}
          onSelect={setSelectedTemplate}
        />
      </section>

      {/* Content Selection */}
      <section className="space-y-4">
        <h3 className="font-semibold">2. Select Content</h3>

        <div>
          <h4 className="text-sm text-muted-foreground mb-2">
            Recommended Capability Blocks
          </h4>
          <BlockSelector
            blocks={matchedBlocks ?? []}
            selected={selectedBlocks}
            onSelect={setSelectedBlocks}
          />
        </div>

        <div>
          <h4 className="text-sm text-muted-foreground mb-2">Case Studies</h4>
          <CaseStudySelector
            studies={caseStudies ?? []}
            selected={selectedStudies}
            onSelect={setSelectedStudies}
          />
        </div>

        <div>
          <h4 className="text-sm text-muted-foreground mb-2">Team Members</h4>
          <TeamSelector
            team={team ?? []}
            selected={selectedTeam}
            onSelect={setSelectedTeam}
          />
        </div>
      </section>

      {/* Actions */}
      <section className="space-y-4">
        <h3 className="font-semibold">3. Generate</h3>
        <button
          onClick={handleAssemble}
          disabled={!selectedTemplate}
          className="w-full px-4 py-2 bg-primary text-primary-foreground rounded disabled:opacity-50"
        >
          Assemble Proposal
        </button>
      </section>
    </div>
  );
}

Seeding Default Content

// convex/templates.ts
export const seedDefaults = mutation({
  args: {},
  handler: async (ctx) => {
    // Check if already seeded
    const existing = await ctx.db.query("capabilityBlocks").first();
    if (existing) return { message: "Already seeded" };

    // Seed capability blocks
    for (const block of DEFAULT_BLOCKS) {
      await ctx.db.insert("capabilityBlocks", {
        ...block,
        lastUpdated: Date.now(),
      });
    }

    // Seed sample case study
    await ctx.db.insert("caseStudies", {
      title: "Federal Agency Cloud Migration",
      client: "US Federal Agency",
      industry: "Government",
      challenge: "Legacy on-premises systems causing operational issues",
      approach: "Phased migration to AWS with serverless architecture",
      results: "50% cost reduction, 99.9% uptime, 3x faster deployments",
      technologies: ["AWS", "Lambda", "DynamoDB", "CloudFront"],
      timeline: "6 months",
      teamSize: 5,
    });

    return { message: "Defaults seeded" };
  },
});
Weekly Installs
4
First Seen
Feb 26, 2026
Installed on
gemini-cli4
github-copilot4
codex4
amp4
kimi-cli4
openclaw4