Skip to main content
Glama

twining_summarize

Generate project summaries with decision counts, open needs, warnings, and recent activity narratives to track development progress.

Instructions

Get a high-level summary of project or scope state. Returns counts of active decisions, open needs, warnings, and a recent activity narrative.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
scopeNoOptional scope filter (default: "project")

Implementation Reference

  • The summarize method in ContextAssembler implements the core logic for the twining_summarize tool, aggregating project/scope state (decisions, blackboard entries, activity).
    async summarize(scope?: string): Promise<SummarizeResult> {
      const targetScope = scope ?? "project";
    
      // Get decisions
      const index = await this.decisionStore.getIndex();
      const scopeIndex = targetScope === "project"
        ? index
        : index.filter(
            (e) =>
              e.scope.startsWith(targetScope) ||
              targetScope.startsWith(e.scope),
          );
    
      const activeDecisions = scopeIndex.filter(
        (e) => e.status === "active",
      ).length;
      const provisionalDecisions = scopeIndex.filter(
        (e) => e.status === "provisional",
      ).length;
    
      // Get blackboard entries
      const readOpts = targetScope === "project" ? undefined : { scope: targetScope };
      const { entries } = await this.blackboardStore.read(readOpts);
    
      const openNeeds = entries.filter((e) => e.entry_type === "need").length;
      const activeWarnings = entries.filter(
        (e) => e.entry_type === "warning",
      ).length;
      const unansweredQuestions = entries.filter(
        (e) => e.entry_type === "question",
      ).length;
    
      // Recent activity (last 24 hours)
      const twentyFourHoursAgo = new Date(
        Date.now() - 24 * 60 * 60 * 1000,
      ).toISOString();
      const recentEntries = entries.filter(
        (e) => e.timestamp >= twentyFourHoursAgo,
      );
      const recentDecisionCount = scopeIndex.filter(
        (e) => e.timestamp >= twentyFourHoursAgo,
      ).length;
      const recentFindingCount = recentEntries.filter(
        (e) => e.entry_type === "finding",
      ).length;
      const recentWarningCount = recentEntries.filter(
        (e) => e.entry_type === "warning",
      ).length;
    
      let recentActivitySummary =
        `In the last 24 hours: ${recentDecisionCount} decision${recentDecisionCount !== 1 ? "s" : ""} made, ` +
        `${recentFindingCount} finding${recentFindingCount !== 1 ? "s" : ""} posted, ` +
        `${recentWarningCount} warning${recentWarningCount !== 1 ? "s" : ""} raised.`;
    
      // Integrate planning state from .planning/ directory
      const planningState = this.planningBridge?.readPlanningState() ?? null;
    
      if (planningState) {
        recentActivitySummary += ` Current phase: ${planningState.current_phase}. Progress: ${planningState.progress}.`;
      }
    
      const result: SummarizeResult = {
        scope: targetScope,
        active_decisions: activeDecisions,
        provisional_decisions: provisionalDecisions,
        open_needs: openNeeds,
        active_warnings: activeWarnings,
        unanswered_questions: unansweredQuestions,
        recent_activity_summary: recentActivitySummary,
      };
    
      if (planningState) {
        result.planning_state = planningState;
      }
    
      return result;
    }
  • The twining_summarize tool is registered here, providing the tool description, input schema, and handler invocation calling ContextAssembler.summarize.
    server.registerTool(
      "twining_summarize",
      {
        description:
          "Get a high-level summary of project or scope state. Returns counts of active decisions, open needs, warnings, and a recent activity narrative.",
        inputSchema: {
          scope: z
            .string()
            .optional()
            .describe('Optional scope filter (default: "project")'),
        },
      },
      async (args) => {
        try {
          const result = await contextAssembler.summarize(args.scope);
          return toolResult(result);
        } catch (e) {
          if (e instanceof TwiningError) {
            return toolError(e.message, e.code);
          }
          return toolError(
            e instanceof Error ? e.message : "Unknown error",
            "INTERNAL_ERROR",
          );
        }
      },
    );

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/daveangulo/twining-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server