UNPKG

aetherlight

Version:

Voice-to-intelligence platform for developers. Voice capture, sprint planning with AI, bug/feature forms, pattern matching to prevent AI hallucinations.

208 lines 9.44 kB
"use strict"; /** * Workflow Enforcement Service * * DESIGN DECISION: Proactive workflow guidance to teach users about middleware features * WHY: Users don't know about analyzeAndPlan when manually creating tasks * * REASONING CHAIN: * 1. User manually creates 3+ tasks in ACTIVE_SPRINT.toml * 2. This is tedious and error-prone (no agent assignment, patterns, validation) * 3. WorkflowEnforcement detects this pattern * 4. Shows suggestion: "Try Analyze & Plan to auto-generate tasks!" * 5. User learns about middleware at the perfect moment * * PATTERN: Pattern-WORKFLOW-INTEGRATION-001 * PATTERN: Pattern-USER-FEEDBACK-001 * RELATED: MID-026 (File Watcher for Manual Task Detection) * * @module services/WorkflowEnforcement */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.WorkflowEnforcement = void 0; const vscode = __importStar(require("vscode")); const path = __importStar(require("path")); const MiddlewareLogger_1 = require("./MiddlewareLogger"); /** * Workflow Enforcement Service * * Detects workflow anti-patterns and suggests better alternatives. */ class WorkflowEnforcement { constructor(context) { this.debounceTimer = null; this.lastTaskCount = 0; this.context = context; this.logger = MiddlewareLogger_1.MiddlewareLogger.getInstance(); } /** * Initialize file watchers * * DESIGN DECISION: Watch ACTIVE_SPRINT.toml for manual task creation + analysis documents for suggestions * WHY: Detect when user is creating tasks manually OR saving analysis docs that could become tasks */ initialize(workspaceRoot) { const sprintPath = path.join(workspaceRoot, 'internal', 'sprints', 'ACTIVE_SPRINT.toml'); this.logger.info('WorkflowEnforcement: Initializing file watchers'); // Watch for text document changes (manual task creation - MID-026) const changeWatcher = vscode.workspace.onDidChangeTextDocument(async (event) => { // Check if the changed document is ACTIVE_SPRINT.toml if (event.document.uri.fsPath.includes('ACTIVE_SPRINT.toml')) { this.onSprintFileChanged(event.document); } }); // Watch for document saves (analysis document detection - MID-027) const saveWatcher = vscode.workspace.onDidSaveTextDocument(async (document) => { await this.onDocumentSaved(document); }); return [changeWatcher, saveWatcher]; } /** * Handle sprint file changes (MID-026) * * DESIGN DECISION: Debounced detection with 3-second delay * WHY: Don't spam suggestions on every keystroke */ onSprintFileChanged(document) { // Clear existing debounce timer if (this.debounceTimer) { clearTimeout(this.debounceTimer); } // Debounce: Wait 3 seconds after last change before checking this.debounceTimer = setTimeout(() => { this.detectManualTaskCreation(document); }, 3000); } /** * Detect manual task creation (MID-026) * * DESIGN DECISION: Count [tasks.XXX] sections, suggest after 3+ * WHY: 1-2 tasks = minor edits, 3+ = user is creating lots of tasks manually */ async detectManualTaskCreation(document) { // Check if user has disabled suggestions const dontShow = this.context.workspaceState.get('workflowEnforcement.dontShowManualTaskSuggestion', false); if (dontShow) { return; } // Count task sections const content = document.getText(); const taskMatches = content.match(/\[tasks\.[A-Z]+-\d+\]/g); const currentTaskCount = taskMatches ? taskMatches.length : 0; // Check if user added 3+ tasks since last check const addedTasks = currentTaskCount - this.lastTaskCount; if (addedTasks >= 3) { this.logger.info(`WorkflowEnforcement: Detected ${addedTasks} new tasks created manually`); // Show suggestion const action = await vscode.window.showInformationMessage(`💡 Creating tasks manually?\n\n` + `You just added ${addedTasks} tasks. Try "Analyze & Plan" to auto-generate tasks with:\n` + `• Agent assignments\n` + `• Pattern recommendations\n` + `• File context\n` + `• Validation criteria`, 'Show Me', 'Continue', 'Don\'t Show Again'); if (action === 'Show Me') { // Open documentation vscode.env.openExternal(vscode.Uri.parse('https://docs.aetherlight.dev/analyze-and-plan')); } else if (action === 'Don\'t Show Again') { // Store preference await this.context.workspaceState.update('workflowEnforcement.dontShowManualTaskSuggestion', true); this.logger.info('WorkflowEnforcement: User disabled manual task creation suggestions'); } } // Update last count this.lastTaskCount = currentTaskCount; } /** * Handle document saved (MID-027) * * DESIGN DECISION: Detect analysis/requirements documents and suggest analyzeAndPlan * WHY: Users save gap analysis, don't know they can auto-generate tasks from it * * Detection criteria: * - Filename contains: 'analysis', 'gaps', 'requirements', 'planning' * - OR content contains headers: '## Tasks', '## Issues', '## Requirements' */ async onDocumentSaved(document) { // Only check markdown files if (document.languageId !== 'markdown') { return; } // Check if suggestion disabled for this file const ignoredFiles = this.context.workspaceState.get('workflowEnforcement.ignoredAnalysisFiles', []); if (ignoredFiles.includes(document.uri.fsPath)) { return; } // Check if this looks like an analysis document const filename = path.basename(document.uri.fsPath).toLowerCase(); const content = document.getText(); const hasAnalysisFilename = filename.includes('analysis') || filename.includes('gaps') || filename.includes('requirements') || filename.includes('planning'); const hasAnalysisHeaders = content.includes('## Tasks') || content.includes('## Issues') || content.includes('## Requirements') || content.includes('## Problems'); if (hasAnalysisFilename || hasAnalysisHeaders) { this.logger.info(`WorkflowEnforcement: Detected analysis document saved: ${filename}`); // Show proactive suggestion const action = await vscode.window.showInformationMessage(`📋 Analysis Document Saved\n\n` + `"${path.basename(document.uri.fsPath)}" looks like an analysis document.\n\n` + `Generate sprint tasks from it automatically?`, 'Analyze & Plan', 'Later', 'Don\'t Suggest for This File'); if (action === 'Analyze & Plan') { // Run analyzeAndPlan command await vscode.commands.executeCommand('aetherlight.analyzeAndPlan'); } else if (action === 'Don\'t Suggest for This File') { // Add file to ignored list const updated = [...ignoredFiles, document.uri.fsPath]; await this.context.workspaceState.update('workflowEnforcement.ignoredAnalysisFiles', updated); this.logger.info(`WorkflowEnforcement: Added ${filename} to ignored list`); } } } /** * Reset suggestion state (for testing) */ async resetSuggestionState() { await this.context.workspaceState.update('workflowEnforcement.dontShowManualTaskSuggestion', false); await this.context.workspaceState.update('workflowEnforcement.ignoredAnalysisFiles', []); this.lastTaskCount = 0; } } exports.WorkflowEnforcement = WorkflowEnforcement; //# sourceMappingURL=WorkflowEnforcement.js.map