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
| Name | Required | Description | Default |
|---|---|---|---|
| scope | No | Optional scope filter (default: "project") |
Implementation Reference
- src/engine/context-assembler.ts:457-533 (handler)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; } - src/tools/context-tools.ts:57-83 (registration)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", ); } }, );