mcp-ai-agent-guidelines
Version:
A comprehensive Model Context Protocol server providing advanced tools, resources, and prompts for implementing AI agent best practices
463 lines (429 loc) • 13.5 kB
JavaScript
/**
* RFCStrategy - Request for Comments document format
*
* Renders domain results as RFC (Request for Comments) documents with
* standard sections for formal technical proposals.
*
* @module strategies/rfc-strategy
* @see {@link https://github.com/Anselmoo/mcp-ai-agent-guidelines/blob/development/plan-v0.13.x/specs/SPEC-001-output-strategy-layer.md SPEC-001} §4.2
*/
import { OutputApproach } from "./output-strategy.js";
/**
* RFCStrategy implements the RFC (Request for Comments) document format.
*
* Supports rendering:
* - PromptResult: Convert prompt analysis to RFC proposal
* - SessionState: Convert design session to RFC document
*
* @implements {OutputStrategy<PromptResult | SessionState>}
*/
export class RFCStrategy {
/** The output approach this strategy implements */
approach = OutputApproach.RFC;
/**
* Render a domain result to RFC format.
*
* @param result - The domain result to render (PromptResult or SessionState)
* @param options - Optional rendering options
* @returns Output artifacts with primary RFC document
* @throws {Error} If result type is not supported
*/
render(result, options) {
if (this.isPromptResult(result)) {
return this.renderPromptAsRFC(result, options);
}
if (this.isSessionState(result)) {
return this.renderSessionAsRFC(result, options);
}
throw new Error("Unsupported domain result type");
}
/**
* Check if this strategy supports rendering a specific domain type.
*
* @param domainType - The domain type identifier
* @returns True if this strategy can render the domain type
*/
supports(domainType) {
return ["PromptResult", "SessionState"].includes(domainType);
}
/**
* Render a PromptResult to RFC format.
*
* @param result - The prompt result to render
* @param options - Optional rendering options
* @returns Output artifacts with RFC document
* @private
*/
renderPromptAsRFC(result, options) {
const title = this.extractTitle(result);
const baseContent = `# RFC: ${title}
## Summary
${this.extractSummary(result)}
## Scope
${this.extractScope(result)}
## Participants
- **Author**: @copilot
- **Reviewers**: TBD
- **Stakeholders**: TBD
## Proposal
${this.extractProposal(result)}
## Pros
${this.extractPros(result)}
## Cons
${this.extractCons(result)}
## Alternatives Considered
${this.extractAlternatives(result)}
## Conclusion
${this.extractConclusion(result)}`;
return {
primary: {
name: "RFC.md",
content: this.formatRFCContent(baseContent, options),
format: "markdown",
},
};
}
/**
* Render a SessionState to RFC format.
*
* @param result - The session state to render
* @param options - Optional rendering options
* @returns Output artifacts with RFC document
* @private
*/
renderSessionAsRFC(result, options) {
const title = this.extractSessionTitle(result);
const baseContent = `# RFC: ${title}
## Summary
${this.extractSessionSummary(result)}
## Scope
${this.extractSessionScope(result)}
## Participants
- **Author**: @copilot
- **Reviewers**: TBD
- **Stakeholders**: TBD
## Status
${result.status || "Draft"}
## Proposal
${this.extractSessionProposal(result)}
## Pros
${this.extractSessionPros(result)}
## Cons
${this.extractSessionCons(result)}
## Alternatives Considered
${this.extractSessionAlternatives(result)}
## Conclusion
${this.extractSessionConclusion(result)}`;
return {
primary: {
name: "RFC.md",
content: this.formatRFCContent(baseContent, options),
format: "markdown",
},
};
}
/**
* Extract title from PromptResult.
*
* @param result - The prompt result
* @returns Extracted title
* @private
*/
extractTitle(result) {
const firstSection = result.sections[0];
return firstSection?.title || "Untitled Proposal";
}
/**
* Extract summary from PromptResult.
*
* @param result - The prompt result
* @returns Extracted summary
* @private
*/
extractSummary(result) {
const firstSection = result.sections[0];
if (firstSection?.body) {
// Take first paragraph or first 200 chars
const firstPara = firstSection.body.split("\n\n")[0];
return firstPara.length > 200
? `${firstPara.substring(0, 200)}...`
: firstPara;
}
return "No summary available.";
}
/**
* Extract scope from PromptResult.
*
* @param result - The prompt result
* @returns Extracted scope
* @private
*/
extractScope(result) {
const scopeSection = result.sections.find((s) => s.title.toLowerCase().includes("scope"));
if (scopeSection?.body) {
return scopeSection.body;
}
return "To be defined";
}
/**
* Extract proposal from PromptResult.
*
* @param result - The prompt result
* @returns Extracted proposal
* @private
*/
extractProposal(result) {
const proposalSection = result.sections.find((s) => s.title.toLowerCase().includes("proposal") ||
s.title.toLowerCase().includes("goal") ||
s.title.toLowerCase().includes("objective"));
if (proposalSection?.body) {
return proposalSection.body;
}
// Use a concise default when no specific proposal section is found
return "To be defined";
}
/**
* Extract pros from PromptResult.
*
* @param result - The prompt result
* @returns Extracted pros as bullet list
* @private
*/
extractPros(result) {
const prosSection = result.sections.find((s) => {
const title = s.title.toLowerCase();
return /\b(pros?|benefits?|advantages?)\b/.test(title);
});
if (prosSection?.body) {
// Convert to bullet list if not already
const lines = prosSection.body.split("\n").filter((l) => l.trim());
return lines.map((l) => (l.startsWith("-") ? l : `- ${l}`)).join("\n");
}
return "- TBD";
}
/**
* Extract cons from PromptResult.
*
* @param result - The prompt result
* @returns Extracted cons as bullet list
* @private
*/
extractCons(result) {
const consSection = result.sections.find((s) => {
const title = s.title.toLowerCase();
return (/\bcons?\b/.test(title) ||
title.includes("drawback") ||
title.includes("disadvantage") ||
title.includes("risk"));
});
if (consSection?.body) {
// Convert to bullet list if not already
const lines = consSection.body.split("\n").filter((l) => l.trim());
return lines.map((l) => (l.startsWith("-") ? l : `- ${l}`)).join("\n");
}
return "- TBD";
}
/**
* Extract alternatives from PromptResult.
*
* @param result - The prompt result
* @returns Extracted alternatives
* @private
*/
extractAlternatives(result) {
const altSection = result.sections.find((s) => s.title.toLowerCase().includes("alternative") ||
s.title.toLowerCase().includes("option"));
if (altSection?.body) {
return altSection.body;
}
return "None documented";
}
/**
* Extract conclusion from PromptResult.
*
* @param result - The prompt result
* @returns Extracted conclusion
* @private
*/
extractConclusion(result) {
const conclusionSection = result.sections.find((s) => s.title.toLowerCase().includes("conclusion") ||
s.title.toLowerCase().includes("recommendation"));
if (conclusionSection?.body) {
return conclusionSection.body;
}
return "Pending team discussion";
}
/**
* Extract title from SessionState.
*
* @param result - The session state
* @returns Extracted title
* @private
*/
extractSessionTitle(result) {
if (result.config?.goal) {
return result.config.goal;
}
if (result.context?.title && typeof result.context.title === "string") {
return result.context.title;
}
return "Untitled Session";
}
/**
* Extract summary from SessionState.
*
* @param result - The session state
* @returns Extracted summary
* @private
*/
extractSessionSummary(result) {
if (result.context?.summary && typeof result.context.summary === "string") {
return result.context.summary;
}
if (result.config?.goal) {
return result.config.goal;
}
return `Design session in ${result.phase} phase`;
}
/**
* Extract scope from SessionState.
*
* @param result - The session state
* @returns Extracted scope
* @private
*/
extractSessionScope(result) {
if (result.context?.scope && typeof result.context.scope === "string") {
return result.context.scope;
}
return `Phase: ${result.phase}`;
}
/**
* Extract proposal from SessionState.
*
* @param result - The session state
* @returns Extracted proposal
* @private
*/
extractSessionProposal(result) {
if (result.context?.proposal &&
typeof result.context.proposal === "string") {
return result.context.proposal;
}
if (result.config?.goal) {
return result.config.goal;
}
if (result.phases) {
const phaseDescriptions = Object.entries(result.phases)
.map(([phase, data]) => `### ${phase}\n\n${JSON.stringify(data, null, 2)}`)
.join("\n\n");
return phaseDescriptions;
}
return "To be defined";
}
/**
* Extract pros from SessionState.
*
* @param result - The session state
* @returns Extracted pros as bullet list
* @private
*/
extractSessionPros(result) {
if (result.context?.pros && Array.isArray(result.context.pros)) {
return result.context.pros.map((p) => `- ${p}`).join("\n");
}
return "- TBD";
}
/**
* Extract cons from SessionState.
*
* @param result - The session state
* @returns Extracted cons as bullet list
* @private
*/
extractSessionCons(result) {
if (result.context?.cons && Array.isArray(result.context.cons)) {
return result.context.cons.map((c) => `- ${c}`).join("\n");
}
return "- TBD";
}
/**
* Extract alternatives from SessionState.
*
* @param result - The session state
* @returns Extracted alternatives
* @private
*/
extractSessionAlternatives(result) {
if (result.context?.alternatives &&
typeof result.context.alternatives === "string") {
return result.context.alternatives;
}
if (result.context?.alternatives &&
Array.isArray(result.context.alternatives)) {
return result.context.alternatives.map((a) => `- ${a}`).join("\n");
}
return "None documented";
}
/**
* Extract conclusion from SessionState.
*
* @param result - The session state
* @returns Extracted conclusion
* @private
*/
extractSessionConclusion(result) {
if (result.context?.conclusion &&
typeof result.context.conclusion === "string") {
return result.context.conclusion;
}
if (result.status === "completed") {
return "Session completed successfully.";
}
return "Pending team discussion";
}
/**
* Format RFC content with optional metadata footer.
*
* @param baseContent - The base RFC content without metadata
* @param options - Optional rendering options
* @returns Formatted content with or without metadata footer
* @private
*/
formatRFCContent(baseContent, options) {
// Only include metadata if explicitly requested (aligning with ChatStrategy)
if (options?.includeMetadata === true) {
return `${baseContent}\n\n---\n*RFC generated: ${new Date().toISOString()}*`;
}
return baseContent;
}
/**
* Type guard for PromptResult.
*
* @param result - The value to check
* @returns True if result is a PromptResult
* @private
*/
isPromptResult(result) {
return (typeof result === "object" &&
result !== null &&
"sections" in result &&
"metadata" in result);
}
/**
* Type guard for SessionState.
*
* @param result - The value to check
* @returns True if result is a SessionState
* @private
*/
isSessionState(result) {
return (typeof result === "object" &&
result !== null &&
"id" in result &&
"phase" in result &&
"context" in result &&
"history" in result);
}
}
//# sourceMappingURL=rfc-strategy.js.map