wogiflow 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,16 +23,31 @@ Execute multiple tasks in sequence, following all workflow rules.
23
23
  **Default behavior** (when `bulkOrchestrator.enabled: true`):
24
24
 
25
25
  1. **Build Execution Plan**:
26
- - Detect dependencies between tasks
26
+ - Detect dependencies between tasks (parent→child, shared files, API contracts)
27
27
  - Group independent tasks into parallel batches
28
28
  - Order batches to respect dependencies
29
29
 
30
- 2. **Execute Each Batch**:
30
+ 2. **Smart Ordering** (when `config.autoCompact.smartOrdering: true`):
31
+ Within each batch, order tasks to maximize context reuse:
32
+ - Analyze which files each task will likely touch (from spec, title, or task type)
33
+ - Group tasks that touch the same files/components together
34
+ - Execute related tasks consecutively to reuse loaded context
35
+ - Example: Tasks touching auth files → tasks touching UI files → tasks touching API files
36
+ - **Dependencies always take priority over context optimization**
37
+
38
+ 3. **Execute Each Batch**:
31
39
  - For each batch, spawn sub-agent(s) with **fresh context**
32
40
  - Independent tasks in same batch run in parallel
33
41
  - Dependent tasks wait for their dependencies
34
42
 
35
- 3. **Pass-Forward Summaries**:
43
+ 4. **Auto-Compact Between Tasks** (when `config.autoCompact.betweenTasks: true`):
44
+ - After completing a task, check: does the NEXT task share context with this one?
45
+ - If YES (same files/components): keep context, proceed immediately
46
+ - If NO (different area of codebase): compact automatically before starting next task
47
+ - **Do NOT ask the user** — compact silently between tasks. This is automatic.
48
+ - This prevents context pollution and saves tokens on long batch runs
49
+
50
+ 5. **Pass-Forward Summaries**:
36
51
  - When Task A completes, generate completion summary
37
52
  - Task B (if dependent on A) receives summary as context
38
53
  - Ensures continuity without context pollution
@@ -0,0 +1,72 @@
1
+ Manage the pending prompts queue — save requests for later while Claude is busy.
2
+
3
+ **Triggers**: `/wogi-pending`, "save this for later", "add to pending"
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ /wogi-pending "fix the header alignment" # Add item to queue
9
+ /wogi-pending --list # Show all pending items
10
+ /wogi-pending --clear 3 # Remove item #3
11
+ /wogi-pending --clear-all # Clear entire queue
12
+ ```
13
+
14
+ ## How It Works
15
+
16
+ ### Adding Items
17
+
18
+ When invoked with a prompt string:
19
+ 1. Read `.workflow/state/pending-prompts.json`
20
+ 2. Append new item with auto-incrementing ID, prompt text, timestamp
21
+ 3. Save file
22
+ 4. Display: "Saved to pending queue (#N). Will process after current task completes."
23
+
24
+ ### Listing Items
25
+
26
+ When invoked with `--list` or no args:
27
+ 1. Read pending-prompts.json
28
+ 2. Display numbered list with timestamps
29
+ 3. Show count: "N items pending"
30
+
31
+ ### Processing (Auto-triggered)
32
+
33
+ After any task completes (via task-completed hook):
34
+ 1. Check if pending-prompts.json has items
35
+ 2. If yes, display:
36
+ ```
37
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
38
+ PENDING QUEUE (N items)
39
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
40
+
41
+ 1. "fix the header alignment" (added 15m ago)
42
+ 2. "API returns 500 on empty input" (added 12m ago)
43
+ 3. "wrong color on submit button" (added 8m ago)
44
+ ...
45
+
46
+ I've analyzed these items for dependencies and grouping:
47
+
48
+ Group A (related — header/UI fixes):
49
+ #1 fix header alignment
50
+ #3 wrong color on submit button
51
+ → Process together as one task
52
+
53
+ Group B (separate — API bug):
54
+ #2 API returns 500 on empty input
55
+ → Process as individual task
56
+
57
+ Proposed order: Group B first (bug fix), then Group A (UI)
58
+
59
+ Ready to start? [Y/adjust/skip]
60
+ ```
61
+ 3. User approves or adjusts
62
+ 4. Each group/item goes through `/wogi-start` individually
63
+
64
+ ### Key Principles
65
+
66
+ - Items are NEVER compressed or merged without user approval
67
+ - Each item preserves its original wording exactly
68
+ - Grouping is suggested but user decides
69
+ - Processing is one-at-a-time through /wogi-start (not one big batch)
70
+ - The queue persists across sessions (saved to disk)
71
+
72
+ ARGUMENTS: {args}
@@ -16,7 +16,11 @@ When invoked with a **quoted request** instead of a task ID, assess intent and r
16
16
 
17
17
  ### Pre-Routing Checks (Automatic)
18
18
 
19
- **Long Input Detection**: If `config.longInputGate.enabled` and prompt > `lineThreshold` (60) lines, auto-invoke `/wogi-extract-review` instead of normal triage. Skip for task IDs or primarily-code prompts.
19
+ **Long Input Detection**: If `config.longInputGate.enabled` and EITHER:
20
+ - Prompt > `lineThreshold` (40) lines, OR
21
+ - Prompt contains 5+ discrete items (numbered lists, bullet points, semicolon-separated requests)
22
+
23
+ → Auto-invoke `/wogi-extract-review` instead of normal triage. This ensures zero-loss extraction of every item. Skip for task IDs or primarily-code prompts.
20
24
 
21
25
  **Plugin Registry Routing**: After command catalog finds no match and `config.plugins.enabled`, check `.workflow/state/plugin-registry.json` for trigger phrase matches (score >= 0.5). Plugin routing has LOWER priority than built-in commands.
22
26
 
@@ -158,6 +162,36 @@ Before generating specs (skip for small tasks ≤2 files, bugfixes, explicit spe
158
162
  - Scope validation, assumption surfacing, edge cases, integration points
159
163
  - Config: `config.clarifyingQuestions`
160
164
 
165
+ ### Step 1.25: Item Reconciliation Gate (Multi-Item Inputs)
166
+
167
+ **Activates when**: User input contains 3+ discrete requests (identified by: numbered lists, bullet points, "and also", "plus", semicolons separating requests, or distinct topics in voice-transcribed text).
168
+
169
+ **Purpose**: Prevent item loss when the AI compresses many requests into fewer stories. This is the #1 cause of "silently dropped items" in long inputs.
170
+
171
+ **Procedure**:
172
+ 1. **Enumerate**: Produce a numbered checklist of EVERY discrete request from the user's input. Each item = one testable action. No compression, no grouping, no summarization.
173
+ 2. **Confirm count**: Display the checklist and count: "I found N items in your request: [list]. Is this complete?"
174
+ 3. **Map to work items**: Each checklist item becomes a trackable acceptance criterion. Items may be grouped into stories, but EVERY item must appear as a criterion in at least one story. No item may be dropped during grouping.
175
+ 4. **Reconciliation check**: After stories/tasks are created, cross-reference: for each original checklist item, verify it appears in at least one acceptance criterion. If any item is missing → add it before proceeding.
176
+ 5. **At completion** (Step 3.5): The criteria verification must trace back to this original checklist. Every checklist item must be verified as implemented.
177
+
178
+ **Example**:
179
+ ```
180
+ User: "Fix the login page, add forgot password, remove mock data,
181
+ update the header logo, and add loading states to all forms"
182
+
183
+ Item Reconciliation:
184
+ 1. Fix the login page [→ Story A, criterion 1]
185
+ 2. Add forgot password flow [→ Story A, criterion 2]
186
+ 3. Remove all mock data [→ Story B, all criteria]
187
+ 4. Update header logo [→ Story C, criterion 1]
188
+ 5. Add loading states to all forms [→ Story C, criterion 2]
189
+
190
+ 5 items found → 5 criteria across 3 stories → 0 items dropped ✓
191
+ ```
192
+
193
+ **Skip when**: Input has only 1-2 items, or is a task ID reference.
194
+
161
195
  ### Step 1.3: Explore Phase (MANDATORY Multi-Agent Research)
162
196
 
163
197
  **For L2+ tasks. Research is MANDATORY** — do NOT skip even if you think you know the answer.
@@ -270,6 +304,31 @@ After implementing all scenarios, BEFORE quality gates:
270
304
 
271
305
  **This prevents "claiming done when not done."**
272
306
 
307
+ ### Step 3.55: Semantic Verification Pass (for "remove/fix all X" tasks)
308
+
309
+ **Activates when**: The task involves removing, cleaning up, or fixing ALL instances of something (e.g., "remove all mock data", "fix all console.log", "replace all hardcoded URLs", "remove all deprecated APIs").
310
+
311
+ **Purpose**: Pattern-based search (regex, grep) finds instances that match a naming pattern. But semantic variants — hardcoded values, helper functions that serve the same purpose, inline fallbacks — are invisible to pattern search. This pass catches what regex misses.
312
+
313
+ **Procedure**:
314
+ 1. After the implementation agent completes, do NOT immediately mark the task as done
315
+ 2. Run a **second verification pass** that asks SEMANTICALLY, not syntactically:
316
+ - "Does any remaining code serve the purpose of [what we're removing]?"
317
+ - NOT: "Does any remaining code match the pattern [MOCK_*]?"
318
+ 3. For each type of removal, use type-specific semantic checks:
319
+
320
+ | Removal Type | Semantic Check |
321
+ |-------------|----------------|
322
+ | Mock data | Scan render output for hardcoded business data (customer names, dollar amounts, percentages, dates that look like test data) |
323
+ | Console.log | Scan for any debugging output (console.warn, console.debug, alert(), debugger statements) |
324
+ | Hardcoded URLs | Scan for string literals containing http://, https://, localhost, IP addresses |
325
+ | Deprecated APIs | Scan for the FUNCTIONALITY the API provided, not just its name |
326
+
327
+ 4. Use AI reasoning, NOT regex — the whole point is catching what regex misses
328
+ 5. Report any findings as additional criteria to fix before completion
329
+
330
+ **Cross-cutting principle**: Pattern matching is for discovery. AI reasoning is for verification. The two-pass approach (pattern search → semantic verification) is the standard for any "fix all X" task.
331
+
273
332
  ### Step 3.6: Integration Wiring Validation (MANDATORY)
274
333
 
275
334
  Run `node node_modules/wogiflow/scripts/flow-wiring-verifier.js wf-XXXXXXXX`
@@ -356,7 +415,7 @@ Reflection: "Have I introduced any bugs or regressions?"
356
415
 
357
416
  **Quality gate keeps failing**: Report, attempt fix, after 3 failures suggest `/wogi-debug-hypothesis`.
358
417
 
359
- **Context too large**: Pre-task check handles proactively. Mid-task: commit progress, suggest `/wogi-compact`.
418
+ **Context too large**: When `config.autoCompact.betweenTasks` is true (default), compact AUTOMATICALLY between tasks — do NOT ask the user. Just do it. Mid-task: commit progress, invoke `/wogi-compact` directly (don't suggest — execute).
360
419
 
361
420
  ## Mandatory Rules
362
421
 
@@ -193,6 +193,12 @@ Determine which test types to run:
193
193
  | No flag | Run based on `config.testing.mode`: `ui`→UI only, `api`→API only, `full`→all 3, `auto`→detect and run applicable |
194
194
 
195
195
  #### Run UI Tests
196
+
197
+ **Pre-check (MANDATORY)**: Before running UI tests, verify the testing tool is available. Do NOT skip this step — it prevents false-negative "not available" reports.
198
+
199
+ 1. If `config.testing.provider` is `playwright-mcp`: Call `ToolSearch("playwright")` to verify Playwright MCP tools are loaded. If tools are found → proceed. If not found → report "Playwright MCP not configured" (distinct from "no test files").
200
+
201
+ 2. Run the test script:
196
202
  ```bash
197
203
  node -e "
198
204
  const { runUITests } = require('./scripts/flow-test-ui');
@@ -200,6 +206,12 @@ runUITests('TASK_ID').then(r => console.log(JSON.stringify(r))).catch(err => con
200
206
  "
201
207
  ```
202
208
 
209
+ 3. **Interpret results separately**:
210
+ - Script returns `reason: 'no-test-files'` → "No test files found. Run `/wogi-test --generate` to create tests."
211
+ - MCP tools not available → "Playwright MCP not configured. Add it to `.claude/settings.json` MCP servers."
212
+ - Both → Show both facts separately, don't merge into one message.
213
+ - Script returns test results → show normally.
214
+
203
215
  #### Run API Tests
204
216
  ```bash
205
217
  node -e "
@@ -158,6 +158,7 @@ See `.claude/docs/commands.md` for complete command reference.
158
158
  | "audit project", "project audit", "full project analysis", "full analysis" | `/wogi-audit` |
159
159
  | "register plugin", "list plugins", "remove plugin", "register MCP" | `/wogi-register` |
160
160
  | "run tests", "test everything", "verify tests", "run the tests", "test this task", "check if it works" | `/wogi-test` |
161
+ | "save for later", "add to pending", "queue this", "pending items", "show pending" | `/wogi-pending` |
161
162
 
162
163
  **IMPORTANT**: When a user's message matches one of these patterns, immediately invoke the Skill tool with the corresponding command. Do not ask for confirmation. These `/wogi-*` commands satisfy the mandatory routing requirement — you do NOT also need to invoke `/wogi-start` when a detection match exists. `/wogi-start` is the fallback for messages that don't match this table.
163
164
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
package/scripts/flow CHANGED
@@ -600,6 +600,9 @@ case "${1:-}" in
600
600
  ;;
601
601
  esac
602
602
  ;;
603
+ pending)
604
+ node "$SCRIPT_DIR/flow-pending.js" "${@:2}"
605
+ ;;
603
606
  queue)
604
607
  node "$SCRIPT_DIR/flow-queue.js" "${@:2}"
605
608
  ;;
@@ -1031,6 +1034,10 @@ case "${1:-}" in
1031
1034
  # Background task execution
1032
1035
  node "$SCRIPT_DIR/flow-background.js" "${@:2}"
1033
1036
  ;;
1037
+ contract-scan)
1038
+ # Contract surface scanner (teams feature)
1039
+ node "$SCRIPT_DIR/flow-contract-scan.js" "${@:2}"
1040
+ ;;
1034
1041
  version|--version|-v)
1035
1042
  show_version
1036
1043
  ;;
@@ -684,7 +684,7 @@ const CONFIG_DEFAULTS = {
684
684
  longInputGate: {
685
685
  enabled: true,
686
686
  charThreshold: 3000,
687
- lineThreshold: 60,
687
+ lineThreshold: 40,
688
688
  smartDefault: true,
689
689
  contentRules: { transcript: 'full', spec: 'full', requirements: 'full', code: 'skip', default: 'quick' },
690
690
  autoTriggerTypes: ['transcript', 'specs', 'requirements', 'feature-request'],
@@ -731,6 +731,33 @@ const CONFIG_DEFAULTS = {
731
731
  phases: ['exploring', 'spec_review', 'scenario', 'criteria_check', 'validating']
732
732
  },
733
733
 
734
+ // --- Damage Control ---
735
+ // --- Auto-Compact (context-aware task scheduling) ---
736
+ autoCompact: {
737
+ betweenTasks: true,
738
+ smartOrdering: true,
739
+ respectDependencies: true
740
+ },
741
+
742
+ // --- Contract Surface (Teams-only — activated on wogi login) ---
743
+ contractSurface: {
744
+ enabled: false,
745
+ projectType: 'auto',
746
+ scanOn: ['sessionStart', 'afterTask'],
747
+ scanners: {
748
+ httpClients: true,
749
+ routes: true,
750
+ events: true,
751
+ sharedTypes: true,
752
+ envVars: true
753
+ },
754
+ httpClientPatterns: ['axios', 'fetch', 'ky', '$fetch'],
755
+ routePatterns: ['express', 'fastify', 'hono', 'next-api'],
756
+ ignoreEndpoints: ['/health', '/metrics', '/favicon.ico'],
757
+ sharedPackages: [],
758
+ maxFiles: 500
759
+ },
760
+
734
761
  // --- Damage Control ---
735
762
  damageControl: {
736
763
  enabled: false,
@@ -132,8 +132,8 @@ const KNOWN_CONFIG_KEYS = [
132
132
  // Session management
133
133
  'metrics', 'requestLog', 'sessionState', 'smartCompaction',
134
134
  // Features (alphabetical)
135
- 'audit', 'bestOfN', 'bugFlow', 'capture',
136
- 'cascade', 'checkpoint', 'commits', 'community',
135
+ 'audit', 'autoCompact', 'bestOfN', 'bugFlow', 'capture',
136
+ 'cascade', 'checkpoint', 'commits', 'community', 'contractSurface',
137
137
  'damageControl', 'decide', 'decisions', 'epics', 'errorRecovery',
138
138
  'eval', 'figmaAnalyzer', 'finalization', 'gateConfidence', 'guidedEdit',
139
139
  'hooks', 'longInputGate', 'lsp', 'mandatorySteps', 'modelAdapters',
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ /**
6
+ * Wogi Flow - Contract Surface Scanner CLI
7
+ *
8
+ * TEAMS-ONLY feature: Scans a project's integration surface and generates
9
+ * contract-surface.json for the wogiflow-cloud orchestration agent.
10
+ *
11
+ * Usage:
12
+ * flow contract-scan # Scan and save to default path
13
+ * flow contract-scan --output <path> # Custom output path
14
+ * flow contract-scan --type backend # Force project type
15
+ * flow contract-scan --json # Output JSON to stdout
16
+ * flow contract-scan --verbose # Show scan progress
17
+ */
18
+
19
+ const fs = require('node:fs');
20
+ const path = require('node:path');
21
+ const { getProjectRoot, getConfig } = require('./flow-utils');
22
+
23
+ const PROJECT_ROOT = getProjectRoot();
24
+ const DEFAULT_OUTPUT = path.join(PROJECT_ROOT, '.workflow', 'state', 'contract-surface.json');
25
+
26
+ function parseArgs(args) {
27
+ const options = {
28
+ output: DEFAULT_OUTPUT,
29
+ type: null,
30
+ json: false,
31
+ verbose: false
32
+ };
33
+
34
+ for (let i = 0; i < args.length; i++) {
35
+ switch (args[i]) {
36
+ case '--output':
37
+ case '-o':
38
+ options.output = args[++i];
39
+ break;
40
+ case '--type':
41
+ case '-t':
42
+ options.type = args[++i];
43
+ break;
44
+ case '--json':
45
+ options.json = true;
46
+ break;
47
+ case '--verbose':
48
+ case '-v':
49
+ options.verbose = true;
50
+ break;
51
+ case '--help':
52
+ case '-h':
53
+ showHelp();
54
+ process.exit(0);
55
+ }
56
+ }
57
+
58
+ return options;
59
+ }
60
+
61
+ function showHelp() {
62
+ console.log(`
63
+ Contract Surface Scanner
64
+
65
+ Scans a project's integration surface (HTTP endpoints, events, shared types,
66
+ environment variables) and generates contract-surface.json.
67
+
68
+ Usage: flow contract-scan [options]
69
+
70
+ Options:
71
+ --output, -o <path> Output path (default: .workflow/state/contract-surface.json)
72
+ --type, -t <type> Force project type (frontend|backend|fullstack|library|monorepo)
73
+ --json Output JSON to stdout instead of saving to file
74
+ --verbose, -v Show scan progress
75
+ --help, -h Show this help
76
+
77
+ Examples:
78
+ flow contract-scan # Scan with defaults
79
+ flow contract-scan --verbose # Scan with progress output
80
+ flow contract-scan --json # Output to stdout
81
+ flow contract-scan --type backend --json # Force type, output to stdout
82
+ `);
83
+ }
84
+
85
+ function main() {
86
+ const args = process.argv.slice(2);
87
+ const options = parseArgs(args);
88
+
89
+ // Check teams config
90
+ const config = getConfig();
91
+ const contractConfig = config.contractSurface || {};
92
+
93
+ if (!contractConfig.enabled && !options.json) {
94
+ console.log('Contract surface scanning is not enabled.');
95
+ console.log('This feature activates when connected to a team (wogi login).');
96
+ console.log('');
97
+ console.log('To scan anyway, use: flow contract-scan --json');
98
+ process.exit(0);
99
+ }
100
+
101
+ // Lazy-load the scanner to keep startup fast
102
+ const { scanContracts } = require('./registries/contract-scanner');
103
+
104
+ const scanOptions = {
105
+ projectName: path.basename(PROJECT_ROOT),
106
+ projectType: options.type || contractConfig.projectType || undefined,
107
+ maxFiles: contractConfig.maxFiles || 500,
108
+ maxDepth: contractConfig.maxDepth || 6,
109
+ verbose: options.verbose
110
+ };
111
+
112
+ if (options.verbose) {
113
+ console.log(`Scanning ${PROJECT_ROOT}...`);
114
+ console.log('');
115
+ }
116
+
117
+ const surface = scanContracts(PROJECT_ROOT, scanOptions);
118
+
119
+ if (options.json) {
120
+ console.log(JSON.stringify(surface, null, 2));
121
+ return;
122
+ }
123
+
124
+ // Save to file
125
+ const outputDir = path.dirname(options.output);
126
+ fs.mkdirSync(outputDir, { recursive: true });
127
+ fs.writeFileSync(options.output, JSON.stringify(surface, null, 2));
128
+
129
+ const relOutput = path.relative(PROJECT_ROOT, options.output);
130
+ console.log(`Contract surface saved to ${relOutput}`);
131
+ console.log('');
132
+ console.log('Summary:');
133
+ console.log(` Project type: ${surface.projectType}`);
134
+ console.log(` Consumed endpoints: ${surface.endpoints.consumes.length}`);
135
+ console.log(` Exposed endpoints: ${surface.endpoints.exposes.length}`);
136
+ console.log(` Event emits: ${surface.events.emits.length}`);
137
+ console.log(` Event listeners: ${surface.events.listensTo.length}`);
138
+ console.log(` Shared type imports:${surface.sharedTypes.imports.length}`);
139
+ console.log(` Shared type exports:${surface.sharedTypes.exports.length}`);
140
+ console.log(` Env vars required: ${surface.environment.requires.length}`);
141
+ console.log(` Env vars defined: ${surface.environment.exposes.length}`);
142
+ }
143
+
144
+ main();
@@ -116,6 +116,8 @@ const PATHS = {
116
116
  epics: path.join(WORKFLOW_DIR, 'epics'),
117
117
  features: path.join(WORKFLOW_DIR, 'features'),
118
118
  plans: path.join(WORKFLOW_DIR, 'plans'),
119
+ // Scratch directory for temporary files (auto-cleaned at session end)
120
+ scratch: path.join(WORKFLOW_DIR, 'scratch'),
119
121
  // Additional workflow directories
120
122
  runs: path.join(WORKFLOW_DIR, 'runs'),
121
123
  checkpoints: path.join(WORKFLOW_DIR, 'checkpoints'),
@@ -0,0 +1,153 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Wogi Flow - Pending Prompts Queue
5
+ *
6
+ * Manages a queue of user requests saved for later processing.
7
+ * Items are saved individually and processed after the current task completes.
8
+ *
9
+ * Usage:
10
+ * flow pending add "prompt text" # Add item to queue
11
+ * flow pending list # Show all pending items
12
+ * flow pending clear [id] # Clear specific item or all
13
+ * flow pending count # Get pending count
14
+ * flow pending mark-processed <id> # Mark item as processed
15
+ */
16
+
17
+ const fs = require('node:fs');
18
+ const path = require('node:path');
19
+ const { PATHS, readJson, writeJson } = require('./flow-utils');
20
+
21
+ const PENDING_PATH = path.join(PATHS.state, 'pending-prompts.json');
22
+
23
+ /**
24
+ * Load pending queue from disk
25
+ * @returns {{ items: Array, nextId: number }}
26
+ */
27
+ function loadPending() {
28
+ return readJson(PENDING_PATH, { items: [], nextId: 1 });
29
+ }
30
+
31
+ /**
32
+ * Save pending queue to disk
33
+ * @param {Object} data - Queue data
34
+ */
35
+ function savePending(data) {
36
+ return writeJson(PENDING_PATH, data);
37
+ }
38
+
39
+ /**
40
+ * Add an item to the pending queue
41
+ * @param {string} prompt - The prompt text to queue
42
+ * @returns {Object} The created item
43
+ */
44
+ function addItem(prompt) {
45
+ const data = loadPending();
46
+ const item = {
47
+ id: data.nextId || (data.items.length + 1),
48
+ prompt: prompt.trim(),
49
+ addedAt: new Date().toISOString(),
50
+ status: 'pending'
51
+ };
52
+ data.items.push(item);
53
+ data.nextId = (data.nextId || data.items.length) + 1;
54
+ savePending(data);
55
+ return item;
56
+ }
57
+
58
+ /**
59
+ * List all pending (non-processed) items
60
+ * @returns {Array} Pending items
61
+ */
62
+ function listItems() {
63
+ const data = loadPending();
64
+ return data.items.filter(i => i.status === 'pending');
65
+ }
66
+
67
+ /**
68
+ * Remove a specific item by ID
69
+ * @param {number} id - Item ID to remove
70
+ * @returns {boolean} Whether the item was found and removed
71
+ */
72
+ function clearItem(id) {
73
+ const data = loadPending();
74
+ const idx = data.items.findIndex(i => i.id === id);
75
+ if (idx === -1) return false;
76
+ data.items.splice(idx, 1);
77
+ savePending(data);
78
+ return true;
79
+ }
80
+
81
+ /**
82
+ * Clear all items from the queue
83
+ * @returns {boolean} Always true
84
+ */
85
+ function clearAll() {
86
+ savePending({ items: [], nextId: 1 });
87
+ return true;
88
+ }
89
+
90
+ /**
91
+ * Get the count of pending (non-processed) items
92
+ * @returns {number}
93
+ */
94
+ function getPendingCount() {
95
+ const data = loadPending();
96
+ return data.items.filter(i => i.status === 'pending').length;
97
+ }
98
+
99
+ /**
100
+ * Mark an item as processed
101
+ * @param {number} id - Item ID to mark
102
+ */
103
+ function markProcessed(id) {
104
+ const data = loadPending();
105
+ const item = data.items.find(i => i.id === id);
106
+ if (item) {
107
+ item.status = 'processed';
108
+ item.processedAt = new Date().toISOString();
109
+ savePending(data);
110
+ }
111
+ }
112
+
113
+ // CLI entry point
114
+ if (require.main === module) {
115
+ const args = process.argv.slice(2);
116
+ const command = args[0];
117
+
118
+ if (command === 'add') {
119
+ const prompt = args.slice(1).join(' ');
120
+ if (!prompt) {
121
+ console.error('Usage: flow pending add "prompt text"');
122
+ process.exit(1);
123
+ }
124
+ const item = addItem(prompt);
125
+ console.log(JSON.stringify({ success: true, item }));
126
+ } else if (command === 'list') {
127
+ const items = listItems();
128
+ console.log(JSON.stringify({ success: true, items, count: items.length }));
129
+ } else if (command === 'clear') {
130
+ const id = parseInt(args[1], 10);
131
+ if (isNaN(id)) {
132
+ clearAll();
133
+ console.log(JSON.stringify({ success: true, cleared: 'all' }));
134
+ } else {
135
+ const removed = clearItem(id);
136
+ console.log(JSON.stringify({ success: removed, cleared: id }));
137
+ }
138
+ } else if (command === 'count') {
139
+ console.log(JSON.stringify({ count: getPendingCount() }));
140
+ } else if (command === 'mark-processed') {
141
+ const id = parseInt(args[1], 10);
142
+ if (isNaN(id)) {
143
+ console.error('Usage: flow pending mark-processed <id>');
144
+ process.exit(1);
145
+ }
146
+ markProcessed(id);
147
+ console.log(JSON.stringify({ success: true, marked: id }));
148
+ } else {
149
+ console.log('Usage: flow pending [add|list|clear|count|mark-processed]');
150
+ }
151
+ }
152
+
153
+ module.exports = { addItem, listItems, clearItem, clearAll, getPendingCount, markProcessed, loadPending, savePending, PENDING_PATH };
@@ -576,7 +576,7 @@ async function runUITests(taskId, options = {}) {
576
576
  if (testFlows.length === 0) {
577
577
  const report = generateReport(
578
578
  taskId,
579
- [{ name: 'No UI tests found', status: 'skipped', expected: [], found: [], missing: [], duration: 0 }],
579
+ [{ name: 'No UI test files found', status: 'skipped', reason: 'no-test-files', expected: [], found: [], missing: [], duration: 0, hint: 'Run /wogi-test --generate to create tests, or /wogi-test-browser for interactive browser testing' }],
580
580
  { checked: stateChecks, covered: [], missing: stateChecks },
581
581
  null
582
582
  );
@@ -119,17 +119,20 @@ function checkClaudeCodeVersionOnce() {
119
119
  return `Claude Code ${version} is below the minimum required version (${HARD_MIN.major}.${HARD_MIN.minor}.${HARD_MIN.patch}). WogiFlow hooks will not work correctly. Update with: npm install -g @anthropic-ai/claude-code@latest`;
120
120
  }
121
121
 
122
- // Soft gates — degrade gracefully, no warning
123
- // 2.1.50+: worktree hooks, agent isolation
124
- // 2.1.72+: ExitWorktree, effort levels, model param on Agent
125
- // 2.1.73+: SessionStart double-fire fix, hook context pollution fix, modelOverrides, subagent model fix
126
- // 2.1.75+: 1M context default (Max/Team/Enterprise), accurate token estimation,
127
- // async hook completion suppressed by default, hook source in permission prompts,
128
- // memory file last-modified timestamps
129
- // 2.1.76+: PostCompact hook (state recovery after compaction), Elicitation/ElicitationResult hooks
130
- // (MCP structured input), worktree.sparsePaths (sparse checkout for monorepos),
131
- // /effort slash command, deferred tools schema fix after compaction,
132
- // auto-compaction circuit breaker (3 retries), background agent partial results preserved
122
+ // Soft gates — generate informational warning listing disabled features
123
+ const SOFT_GATES = [
124
+ { version: [2, 1, 50], features: 'worktree hooks, agent isolation' },
125
+ { version: [2, 1, 72], features: 'ConfigChange/InstructionsLoaded hooks, effort levels' },
126
+ { version: [2, 1, 76], features: 'PostCompact hook (state recovery after compaction)' },
127
+ ];
128
+
129
+ const disabledFeatures = SOFT_GATES
130
+ .filter(gate => !meetsVersion(major, minor, patch, ...gate.version))
131
+ .map(gate => ` - ${gate.features} (requires ${gate.version.join('.')}+)`);
132
+
133
+ if (disabledFeatures.length > 0) {
134
+ return `Claude Code ${version} — some WogiFlow features are disabled:\n${disabledFeatures.join('\n')}\nUpdate for full functionality: npm i -g @anthropic-ai/claude-code@latest`;
135
+ }
133
136
 
134
137
  return null;
135
138
  }