wogiflow 1.1.4 → 1.1.6

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.
@@ -141,3 +141,36 @@ execFileSync('sg', ['--pattern', pattern, '--lang', lang, '--json', path]);
141
141
  - Prefer `execFile`/`execFileSync` with array arguments over `exec`/`execSync` with template strings
142
142
  - When using template strings, escape all user-controlled values
143
143
  - Never interpolate user input directly into shell commands
144
+
145
+ ## 9. Temp Directory Isolation (Claude Code 2.1.23+)
146
+
147
+ On shared systems (CI servers, multi-user machines), use per-user temp directories to prevent permission conflicts.
148
+
149
+ **Fixed in Claude Code 2.1.23**: Per-user temp directory isolation prevents permission conflicts.
150
+
151
+ ```javascript
152
+ // Good - per-user isolation
153
+ const userId = process.getuid?.() ?? process.env.USER ?? process.env.USERNAME ?? 'default';
154
+ const tempDir = path.join(os.tmpdir(), `myapp-${userId}`);
155
+
156
+ // Bad - global temp path on shared systems
157
+ const tempDir = path.join(os.tmpdir(), 'myapp');
158
+ ```
159
+
160
+ **Best practices:**
161
+ - Use UID on Unix systems (`process.getuid()`)
162
+ - Fall back to username environment variables on Windows
163
+ - Always provide a 'default' fallback for edge cases
164
+ - This pattern is used in `flow-worktree.js` for worktree isolation
165
+
166
+ ## 10. Search/Grep Timeout Handling (Claude Code 2.1.23+)
167
+
168
+ **Fixed in Claude Code 2.1.23**: Ripgrep search timeouts now report errors instead of silently returning empty results.
169
+
170
+ **Impact on WogiFlow:** Component detection, auto-context loading, and pattern matching rely on search operations. Before 2.1.23, search timeouts could cause false negatives.
171
+
172
+ **Best practices:**
173
+ - Handle empty search results gracefully - they may indicate timeout
174
+ - Add retry logic for search-dependent operations
175
+ - Log warnings when searches return unexpectedly empty
176
+ - Consider fallback strategies (glob-based search if grep fails)
@@ -590,13 +590,18 @@ class BaseBridge {
590
590
  * @returns {string} Content with conditionals evaluated
591
591
  */
592
592
  processConditionals(content, config) {
593
- const ifRegex = /\{\{#if\s+([^}]+)\}\}([\s\S]*?)\{\{\/if\}\}/g;
593
+ // Match INNERMOST conditionals first - those with no nested {{#if}} in body
594
+ // The negative lookahead (?!{{#if) ensures body contains no nested conditionals
595
+ const innerIfRegex = /\{\{#if\s+([^}]+)\}\}((?:(?!\{\{#if)[\s\S])*?)\{\{\/if\}\}/g;
594
596
 
595
597
  let lastContent;
598
+ let iterations = 0;
599
+ const maxIterations = 100; // Safety limit for deeply nested templates
600
+
596
601
  // Keep processing until no more changes (handles nested conditions)
597
602
  do {
598
603
  lastContent = content;
599
- content = content.replace(ifRegex, (match, condition, body) => {
604
+ content = content.replace(innerIfRegex, (match, condition, body) => {
600
605
  let value;
601
606
  if (condition.startsWith('config.')) {
602
607
  value = this.getNestedValue(config, condition.replace('config.', ''));
@@ -607,7 +612,12 @@ class BaseBridge {
607
612
  }
608
613
  return value ? body : '';
609
614
  });
610
- } while (content !== lastContent);
615
+ iterations++;
616
+ } while (content !== lastContent && iterations < maxIterations);
617
+
618
+ if (iterations >= maxIterations) {
619
+ this.log('Warning: Max iterations reached in processConditionals - possible malformed template');
620
+ }
611
621
 
612
622
  return content;
613
623
  }
@@ -419,7 +419,7 @@ Last synced: ${new Date().toISOString()}
419
419
  }
420
420
  }
421
421
 
422
- return {
422
+ const settings = {
423
423
  permissions: {
424
424
  allow: wildcardPermissions,
425
425
  },
@@ -428,6 +428,20 @@ Last synced: ${new Date().toISOString()}
428
428
  _wogiFlowVersion: '2.0.0',
429
429
  _generatedAt: new Date().toISOString(),
430
430
  };
431
+
432
+ // Add spinnerVerbs if configured (requires Claude Code 2.1.23+)
433
+ const spinnerVerbs = config.hooks?.claudeCode?.spinnerVerbs;
434
+ if (Array.isArray(spinnerVerbs) && spinnerVerbs.length > 0) {
435
+ settings.spinnerVerbs = spinnerVerbs;
436
+ }
437
+
438
+ // Add spinnerTipsEnabled if explicitly set (default is true in Claude Code)
439
+ const spinnerTipsEnabled = config.hooks?.claudeCode?.spinnerTipsEnabled;
440
+ if (spinnerTipsEnabled === false) {
441
+ settings.spinnerTipsEnabled = false;
442
+ }
443
+
444
+ return settings;
431
445
  }
432
446
 
433
447
  /**
@@ -448,7 +462,7 @@ Last synced: ${new Date().toISOString()}
448
462
 
449
463
  const newSettings = this.generateSettings(config);
450
464
 
451
- // Merge: keep existing hooks, use new permissions
465
+ // Merge: keep existing hooks, use new permissions and spinner settings
452
466
  const mergedSettings = {
453
467
  permissions: newSettings.permissions,
454
468
  respectGitignore: newSettings.respectGitignore,
@@ -458,6 +472,16 @@ Last synced: ${new Date().toISOString()}
458
472
  _generatedAt: newSettings._generatedAt,
459
473
  };
460
474
 
475
+ // Include spinnerVerbs if configured
476
+ if (newSettings.spinnerVerbs) {
477
+ mergedSettings.spinnerVerbs = newSettings.spinnerVerbs;
478
+ }
479
+
480
+ // Include spinnerTipsEnabled if explicitly disabled
481
+ if (newSettings.spinnerTipsEnabled === false) {
482
+ mergedSettings.spinnerTipsEnabled = false;
483
+ }
484
+
461
485
  fs.writeFileSync(settingsPath, JSON.stringify(mergedSettings, null, 2));
462
486
  this.log(`Synced settings.local.json with wildcard permissions (${newSettings.permissions.allow.length} rules)`);
463
487
 
@@ -538,6 +538,38 @@ When corrected:
538
538
  - Individual records stored in `.workflow/corrections/` for searchable history
539
539
  - Aggregated patterns in `feedback-patterns.md` for AI context
540
540
 
541
+ ### Repeated Issue Detection (CRITICAL)
542
+
543
+ **When you detect user frustration about a repeated issue, you MUST act immediately.**
544
+
545
+ The user may express this in many ways:
546
+ - "This keeps happening"
547
+ - "I told you this before"
548
+ - "You keep forgetting X"
549
+ - "This failed again"
550
+ - "How many times do I have to tell you"
551
+ - Any indication of a recurring problem or previous instruction being ignored
552
+
553
+ **Required response:**
554
+
555
+ 1. **Acknowledge** - Don't be defensive. The user's frustration is valid.
556
+
557
+ 2. **Investigate root cause** - Don't just fix the symptom:
558
+ - Why did this happen multiple times?
559
+ - What instruction or rule was missing?
560
+ - Check `decisions.md` and `feedback-patterns.md` for related issues
561
+
562
+ 3. **Persist the fix to `decisions.md`** - This is MANDATORY:
563
+ - Add a clear rule that prevents recurrence
564
+ - Include context about why it matters
565
+ - Add verification steps if applicable
566
+
567
+ 4. **Record in `feedback-patterns.md`** - Track the pattern with count and mark as promoted
568
+
569
+ 5. **Verify the fix** - Don't just write the rule; test that it works
570
+
571
+ **Why this matters**: The user installed WogiFlow expecting an AI that learns and improves. Repeating the same mistakes breaks trust. Every repeated failure is a failure of the learning system itself.
572
+
541
573
  ### Improvement Placement
542
574
 
543
575
  Before implementing, determine scope:
package/README.md CHANGED
@@ -19,14 +19,16 @@ npx flow bridge sync
19
19
 
20
20
  WogiFlow works with 6 AI coding CLIs. Use whichever you prefer - the workflow state is shared.
21
21
 
22
- | CLI | Enforcement | Rules File | Guide |
23
- |-----|-------------|------------|-------|
24
- | **Claude Code** | Hard (hooks) | `CLAUDE.md` | [Guide](.workflow/docs/cli-guides/claude-code.md) |
25
- | **Gemini CLI** | Hard (hooks) | `GEMINI.md` | [Guide](.workflow/docs/cli-guides/gemini-cli.md) |
26
- | **Cursor** | Mixed | `.cursor/rules/wogiflow.mdc` | [Guide](.workflow/docs/cli-guides/cursor.md) |
27
- | **OpenCode** | Hard (plugins) | `AGENTS.md` | [Guide](.workflow/docs/cli-guides/opencode.md) |
28
- | **Codex** | Soft (rules) | `AGENTS.md` | [Guide](.workflow/docs/cli-guides/codex.md) |
29
- | **Kimi** | Soft (rules) | `AGENTS.md` | [Guide](.workflow/docs/cli-guides/kimi.md) |
22
+ | CLI | Enforcement | Rules File | Min Version | Guide |
23
+ |-----|-------------|------------|-------------|-------|
24
+ | **Claude Code** | Hard (hooks) | `CLAUDE.md` | **2.1.23+** | [Guide](.workflow/docs/cli-guides/claude-code.md) |
25
+ | **Gemini CLI** | Hard (hooks) | `GEMINI.md` | - | [Guide](.workflow/docs/cli-guides/gemini-cli.md) |
26
+ | **Cursor** | Mixed | `.cursor/rules/wogiflow.mdc` | - | [Guide](.workflow/docs/cli-guides/cursor.md) |
27
+ | **OpenCode** | Hard (plugins) | `AGENTS.md` | - | [Guide](.workflow/docs/cli-guides/opencode.md) |
28
+ | **Codex** | Soft (rules) | `AGENTS.md` | - | [Guide](.workflow/docs/cli-guides/codex.md) |
29
+ | **Kimi** | Soft (rules) | `AGENTS.md` | - | [Guide](.workflow/docs/cli-guides/kimi.md) |
30
+
31
+ > **Claude Code 2.1.23+ Recommended**: Includes critical fixes for per-user temp directory isolation (shared systems), async hook cancellation, and ripgrep timeout reporting. Earlier versions may experience silent search failures.
30
32
 
31
33
  **Enforcement levels:**
32
34
  - **Hard**: Blocks operations before execution (best protection)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -38,6 +38,39 @@ const {
38
38
  checkSpecMigration
39
39
  } = require('./flow-utils');
40
40
 
41
+ const { execSync } = require('child_process');
42
+
43
+ /**
44
+ * Check Claude Code version and compare against minimum recommended (2.1.23)
45
+ * @returns {{ version: string|null, meetsMinimum: boolean }}
46
+ */
47
+ function checkClaudeCodeVersion() {
48
+ try {
49
+ const output = execSync('claude --version 2>/dev/null || echo ""', {
50
+ encoding: 'utf-8',
51
+ stdio: ['pipe', 'pipe', 'pipe']
52
+ }).trim();
53
+
54
+ // Parse version from output like "claude 2.1.23" or "Claude Code 2.1.23"
55
+ const match = output.match(/(\d+\.\d+\.\d+)/);
56
+ if (!match) {
57
+ return { version: null, meetsMinimum: true };
58
+ }
59
+
60
+ const version = match[1];
61
+ const [major, minor, patch] = version.split('.').map(Number);
62
+
63
+ // Minimum recommended: 2.1.23
64
+ const meetsMinimum = major > 2 ||
65
+ (major === 2 && minor > 1) ||
66
+ (major === 2 && minor === 1 && patch >= 23);
67
+
68
+ return { version, meetsMinimum };
69
+ } catch {
70
+ return { version: null, meetsMinimum: true };
71
+ }
72
+ }
73
+
41
74
  function main() {
42
75
  console.log(color('cyan', 'Wogi Flow Health Check'));
43
76
  console.log('========================');
@@ -90,6 +123,20 @@ function main() {
90
123
  issues++;
91
124
  }
92
125
 
126
+ // Check Claude Code version (if applicable)
127
+ if (cliType === 'claude-code') {
128
+ const versionCheck = checkClaudeCodeVersion();
129
+ if (versionCheck.version) {
130
+ if (versionCheck.meetsMinimum) {
131
+ console.log(` ${color('green', '✓')} Claude Code version: ${versionCheck.version}`);
132
+ } else {
133
+ console.log(` ${color('yellow', '○')} Claude Code version: ${versionCheck.version} (2.1.23+ recommended)`);
134
+ console.log(` ${color('dim', '→ Older versions may have silent search failures and shared system issues')}`);
135
+ warnings++;
136
+ }
137
+ }
138
+ }
139
+
93
140
  // Check required directories
94
141
  console.log('');
95
142
  printSection('Checking directories...');
@@ -3024,6 +3024,101 @@ function checkSpecMigration() {
3024
3024
  return needsMigration;
3025
3025
  }
3026
3026
 
3027
+ // ============================================================
3028
+ // Safe Search Utilities (Claude Code 2.1.23+ compatibility)
3029
+ // ============================================================
3030
+
3031
+ /**
3032
+ * Perform a safe grep search with proper timeout and error handling.
3033
+ * Returns results and metadata about search status.
3034
+ *
3035
+ * Before Claude Code 2.1.23, ripgrep timeouts would silently return empty results.
3036
+ * This utility provides explicit handling for timeouts and errors.
3037
+ *
3038
+ * @param {string} pattern - Search pattern
3039
+ * @param {Object} options - Search options
3040
+ * @param {string} [options.path] - Directory to search (default: PROJECT_ROOT)
3041
+ * @param {string[]} [options.extensions] - File extensions to include (e.g., ['ts', 'tsx'])
3042
+ * @param {number} [options.timeout] - Timeout in ms (default: 10000)
3043
+ * @param {number} [options.maxResults] - Maximum results to return (default: 50)
3044
+ * @param {boolean} [options.caseInsensitive] - Case insensitive search (default: true)
3045
+ * @returns {{ results: string[], timedOut: boolean, error: string|null }}
3046
+ */
3047
+ function safeGrepSearch(pattern, options = {}) {
3048
+ const { spawnSync } = require('child_process');
3049
+
3050
+ const searchPath = options.path || PROJECT_ROOT;
3051
+ const extensions = options.extensions || ['ts', 'tsx', 'js', 'jsx'];
3052
+ const timeout = options.timeout || 10000;
3053
+ const maxResults = options.maxResults || 50;
3054
+ const caseInsensitive = options.caseInsensitive !== false;
3055
+
3056
+ const args = ['-rl'];
3057
+ if (caseInsensitive) args.push('-i');
3058
+
3059
+ // Add include patterns for extensions
3060
+ for (const ext of extensions) {
3061
+ args.push(`--include=*.${ext}`);
3062
+ }
3063
+
3064
+ args.push(pattern, searchPath);
3065
+
3066
+ try {
3067
+ const result = spawnSync('grep', args, {
3068
+ encoding: 'utf-8',
3069
+ timeout,
3070
+ stdio: ['pipe', 'pipe', 'pipe']
3071
+ });
3072
+
3073
+ // Check for timeout
3074
+ if (result.signal === 'SIGTERM') {
3075
+ return {
3076
+ results: [],
3077
+ timedOut: true,
3078
+ error: `Search timed out after ${timeout}ms`
3079
+ };
3080
+ }
3081
+
3082
+ // Check for error (but exit code 1 just means no matches)
3083
+ if (result.status > 1) {
3084
+ return {
3085
+ results: [],
3086
+ timedOut: false,
3087
+ error: result.stderr?.trim() || `grep exited with code ${result.status}`
3088
+ };
3089
+ }
3090
+
3091
+ const files = (result.stdout || '')
3092
+ .split('\n')
3093
+ .filter(f => f.trim())
3094
+ .slice(0, maxResults);
3095
+
3096
+ return {
3097
+ results: files,
3098
+ timedOut: false,
3099
+ error: null
3100
+ };
3101
+ } catch (err) {
3102
+ // Handle ETIMEDOUT and other errors
3103
+ const isTimeout = err.code === 'ETIMEDOUT' || err.killed;
3104
+ return {
3105
+ results: [],
3106
+ timedOut: isTimeout,
3107
+ error: isTimeout ? `Search timed out after ${timeout}ms` : err.message
3108
+ };
3109
+ }
3110
+ }
3111
+
3112
+ /**
3113
+ * Configuration defaults for search operations
3114
+ */
3115
+ const SEARCH_DEFAULTS = {
3116
+ timeout: 10000, // 10 seconds
3117
+ maxResults: 50, // Maximum files to return
3118
+ retryOnTimeout: true, // Whether to retry with reduced scope on timeout
3119
+ extensions: ['ts', 'tsx', 'js', 'jsx', 'vue', 'svelte']
3120
+ };
3121
+
3027
3122
  // ============================================================
3028
3123
  // Exports
3029
3124
  // ============================================================
@@ -3172,6 +3267,10 @@ module.exports = {
3172
3267
  findTaskInAllLists,
3173
3268
  analyzeRequest,
3174
3269
  estimateComplexity,
3270
+
3271
+ // Safe Search (Claude Code 2.1.23+ compatibility)
3272
+ safeGrepSearch,
3273
+ SEARCH_DEFAULTS,
3175
3274
  };
3176
3275
 
3177
3276
  // ============================================================
@@ -33,7 +33,19 @@ const { sanitizeCommitMessage } = require('./flow-security');
33
33
  // ============================================================
34
34
 
35
35
  const WORKTREE_PREFIX = 'wogi-task-';
36
- const WORKTREE_BASE_DIR = path.join(os.tmpdir(), 'wogi-worktrees');
36
+
37
+ /**
38
+ * Get user-specific worktree base directory.
39
+ * Uses UID on Unix, username on Windows, with 'default' fallback.
40
+ * This prevents permission conflicts on shared systems (CI, multi-user machines).
41
+ * See: Claude Code 2.1.23 per-user temp directory isolation fix.
42
+ */
43
+ function getWorktreeBaseDir() {
44
+ const userId = process.getuid?.() ?? process.env.USER ?? process.env.USERNAME ?? 'default';
45
+ return path.join(os.tmpdir(), `wogi-worktrees-${userId}`);
46
+ }
47
+
48
+ const WORKTREE_BASE_DIR = getWorktreeBaseDir();
37
49
 
38
50
  // ============================================================
39
51
  // Helper Functions