pursuit-brief

This skill generates structured 1-page pursuit briefs to support rapid bid/no-bid decisions for RFP opportunities.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "pursuit-brief" with this command: npx skills add atemndobs/nebula-rfp/atemndobs-nebula-rfp-pursuit-brief

Pursuit Brief Skill

Overview

This skill generates structured 1-page pursuit briefs to support rapid bid/no-bid decisions for RFP opportunities.

Brief Structure

interface PursuitBrief { rfpId: string; quickFacts: QuickFacts; chaseabilityScore: ChaseabilityScore; summary: string; whyPursue: string[]; risks: string[]; eligibility: EligibilityChecklist; recommendedTeam: TeamRecommendation; winStrategyNotes: string; }

interface QuickFacts { source: string; noticeId: string; agency: string; posted: Date; deadline: Date; daysRemaining: number; estValue: string; location: string; setAside?: string; }

interface TeamRecommendation { technicalLead: boolean; frontendDevs: number; backendDevs: number; fullStackDevs: number; devOps: boolean; qa: boolean; designer: boolean; }

Template (Markdown)

Pursuit Brief: {{RFP_TITLE}}

Quick Facts

FieldValue
Source{{SOURCE}}
Notice ID{{NOTICE_ID}}
Agency{{AGENCY}}
Posted{{POSTED_DATE}}
Deadline{{DEADLINE}} ({{DAYS_REMAINING}} days)
Est. Value{{EST_VALUE}}
Location{{LOCATION}}
Set-Aside{{SET_ASIDE}}

Chaseability Score: {{SCORE}}/100

Score Breakdown

  • Technical Relevance: {{TECH_SCORE}}/25
  • Scope Fit: {{SCOPE_SCORE}}/20
  • Category Focus: {{CATEGORY_SCORE}}/15
  • Client Profile: {{CLIENT_SCORE}}/15
  • Logistics: {{LOGISTICS_SCORE}}/15
  • Skill Alignment: {{SKILL_SCORE}}/10

Opportunity Summary

{{SUMMARY}}

Why Pursue

  • {{STRENGTH_1}}
  • {{STRENGTH_2}}
  • {{STRENGTH_3}}

Risks & Concerns

  • {{RISK_1}}
  • {{RISK_2}}
  • {{RISK_3}}

Eligibility Status

  • [{{US_ORG_STATUS}}] US Organization Requirement
  • [{{CLEARANCE_STATUS}}] Security Clearance
  • [{{ONSITE_STATUS}}] Onsite Presence

Recommended Team

RoleCount
Technical Lead1
Frontend Dev{{FE_COUNT}}
Backend Dev{{BE_COUNT}}
Full-Stack Dev{{FS_COUNT}}
DevOps{{DEVOPS}}
QA Engineer{{QA}}

Win Strategy Notes

{{WIN_STRATEGY}}


Decision

  • PURSUE - Strong fit, move to capture
  • MAYBE - Needs more investigation
  • SKIP - Does not meet criteria

Decision By: _________________ Date: _________

Generation Logic

Convex Action

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

export const generateBrief = action({ args: { rfpId: v.id("rfps") }, handler: async (ctx, args) => { // Get RFP and evaluation const rfp = await ctx.runQuery(internal.rfps.get, { id: args.rfpId }); const evaluation = await ctx.runQuery(internal.evaluations.getByRfp, { rfpId: args.rfpId, });

if (!rfp) throw new Error("RFP not found");
if (!evaluation) throw new Error("Evaluate RFP first");

// Build quick facts
const quickFacts: QuickFacts = {
  source: rfp.source,
  noticeId: rfp.externalId,
  agency: extractAgency(rfp),
  posted: new Date(rfp.postedDate),
  deadline: new Date(rfp.expiryDate),
  daysRemaining: Math.ceil(
    (rfp.expiryDate - Date.now()) / (1000 * 60 * 60 * 24)
  ),
  estValue: rfp.rawData?.estimatedValue ?? "Not specified",
  location: rfp.location,
  setAside: rfp.setAside,
};

// Generate AI sections
const aiSections = await generateAISections(rfp, evaluation);

// Infer team needs
const recommendedTeam = inferTeamNeeds(rfp);

// Build brief
const brief: PursuitBrief = {
  rfpId: args.rfpId,
  quickFacts,
  chaseabilityScore: {
    overall: evaluation.score,
    breakdown: buildBreakdown(evaluation),
  },
  summary: aiSections.summary,
  whyPursue: aiSections.strengths,
  risks: aiSections.risks,
  eligibility: {
    usOrg: evaluation.eligibility.status === "ok" ? "✓" : "!",
    clearance: "✓",
    onsite: "✓",
  },
  recommendedTeam,
  winStrategyNotes: aiSections.winStrategy,
};

// Save brief to pursuit
await ctx.runMutation(internal.pursuits.saveBrief, {
  rfpId: args.rfpId,
  brief: JSON.stringify(brief),
});

return brief;

}, });

AI Section Generation

async function generateAISections( rfp: RFP, evaluation: Evaluation ): Promise<{ summary: string; strengths: string[]; risks: string[]; winStrategy: string; }> { const prompt = ` Analyze this RFP opportunity for Nebula Logix, a cloud-native software company specializing in serverless architectures, APIs, and workflow systems.

RFP Title: ${rfp.title} Description: ${rfp.description} Evaluation Score: ${evaluation.score}/100 Matched Keywords: ${evaluation.criteriaResults .flatMap((c) => c.matchedKeywords) .join(", ")}

Provide:

  1. A 2-3 sentence summary of what the client needs
  2. 3 specific reasons why we should pursue this (based on our strengths)
  3. 3 potential risks or concerns
  4. Initial win strategy notes (how we differentiate, competition considerations)

Respond with JSON only: { "summary": "...", "strengths": ["...", "...", "..."], "risks": ["...", "...", "..."], "winStrategy": "..." }`;

const response = await callAIProvider(prompt); return JSON.parse(response); }

Team Inference

function inferTeamNeeds(rfp: RFP): TeamRecommendation { const text = ${rfp.title} ${rfp.description}.toLowerCase();

const needs: TeamRecommendation = { technicalLead: true, frontendDevs: 0, backendDevs: 0, fullStackDevs: 0, devOps: false, qa: false, designer: false, };

// Frontend indicators if ( text.includes("frontend") || text.includes("react") || text.includes("user interface") || text.includes("ui/ux") ) { needs.frontendDevs = 1; }

// Backend indicators if ( text.includes("backend") || text.includes("api") || text.includes("database") || text.includes("server") ) { needs.backendDevs = 1; }

// Full-stack or general if (text.includes("full-stack") || text.includes("full stack")) { needs.fullStackDevs = 2; }

// DevOps indicators if ( text.includes("devops") || text.includes("ci/cd") || text.includes("deployment") || text.includes("infrastructure") ) { needs.devOps = true; }

// QA indicators if ( text.includes("testing") || text.includes("qa") || text.includes("quality assurance") ) { needs.qa = true; }

// Design indicators if ( text.includes("design") || text.includes("ux") || text.includes("user experience") ) { needs.designer = true; }

// Default if nothing detected if (needs.frontendDevs + needs.backendDevs + needs.fullStackDevs === 0) { needs.fullStackDevs = 2; }

return needs; }

UI Component

// components/PursuitBriefView.tsx import { useQuery, useMutation } from "convex/react"; import { api } from "../convex/_generated/api";

export function PursuitBriefView({ rfpId }: { rfpId: Id<"rfps"> }) { const pursuit = useQuery(api.pursuits.getByRfp, { rfpId }); const generateBrief = useMutation(api.pursuits.generateBrief); const updateDecision = useMutation(api.pursuits.updateDecision);

const brief = pursuit?.brief ? JSON.parse(pursuit.brief) : null;

if (!brief) { return ( <div className="p-6 text-center"> <p className="text-muted-foreground mb-4"> No pursuit brief generated yet. </p> <button onClick={() => generateBrief({ rfpId })} className="px-4 py-2 bg-primary text-primary-foreground rounded" > Generate Pursuit Brief </button> </div> ); }

return ( <div className="p-6 max-w-4xl mx-auto space-y-6"> <h1 className="text-2xl font-bold"> Pursuit Brief: {brief.quickFacts.agency} </h1>

  {/* Quick Facts Table */}
  &#x3C;QuickFactsTable facts={brief.quickFacts} />

  {/* Score Display */}
  &#x3C;ScoreCard score={brief.chaseabilityScore} />

  {/* Summary */}
  &#x3C;section>
    &#x3C;h2 className="text-lg font-semibold mb-2">Summary&#x3C;/h2>
    &#x3C;p className="text-muted-foreground">{brief.summary}&#x3C;/p>
  &#x3C;/section>

  {/* Why Pursue */}
  &#x3C;section>
    &#x3C;h2 className="text-lg font-semibold mb-2">Why Pursue&#x3C;/h2>
    &#x3C;ul className="list-disc list-inside space-y-1">
      {brief.whyPursue.map((item, i) => (
        &#x3C;li key={i} className="text-muted-foreground">{item}&#x3C;/li>
      ))}
    &#x3C;/ul>
  &#x3C;/section>

  {/* Risks */}
  &#x3C;section>
    &#x3C;h2 className="text-lg font-semibold mb-2">Risks &#x26; Concerns&#x3C;/h2>
    &#x3C;ul className="list-disc list-inside space-y-1">
      {brief.risks.map((item, i) => (
        &#x3C;li key={i} className="text-muted-foreground">{item}&#x3C;/li>
      ))}
    &#x3C;/ul>
  &#x3C;/section>

  {/* Decision Buttons */}
  &#x3C;div className="flex gap-4 pt-4 border-t">
    &#x3C;button
      onClick={() => updateDecision({ rfpId, decision: "pursue" })}
      className="px-6 py-2 bg-success text-white rounded font-medium"
    >
      PURSUE
    &#x3C;/button>
    &#x3C;button
      onClick={() => updateDecision({ rfpId, decision: "maybe" })}
      className="px-6 py-2 bg-warning text-white rounded font-medium"
    >
      MAYBE
    &#x3C;/button>
    &#x3C;button
      onClick={() => updateDecision({ rfpId, decision: "skip" })}
      className="px-6 py-2 bg-destructive text-white rounded font-medium"
    >
      SKIP
    &#x3C;/button>
  &#x3C;/div>
&#x3C;/div>

); }

Export Functions

export function exportBriefAsMarkdown(brief: PursuitBrief): string { return `# Pursuit Brief: ${brief.quickFacts.agency}

Quick Facts

FieldValue
Source${brief.quickFacts.source}
Notice ID${brief.quickFacts.noticeId}
Deadline${formatDate(brief.quickFacts.deadline)} (${brief.quickFacts.daysRemaining} days)
Location${brief.quickFacts.location}

Chaseability Score: ${brief.chaseabilityScore.overall}/100

Summary

${brief.summary}

Why Pursue

${brief.whyPursue.map((s) => - ${s}).join("\n")}

Risks

${brief.risks.map((r) => - ${r}).join("\n")}

Win Strategy

${brief.winStrategyNotes} `; }

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

csv-export

No summary provided by upstream source.

Repository SourceNeeds Review
General

rfp-evaluate

No summary provided by upstream source.

Repository SourceNeeds Review
General

proposal-builder

No summary provided by upstream source.

Repository SourceNeeds Review
General

compliance-matrix

No summary provided by upstream source.

Repository SourceNeeds Review