wogiflow 2.17.0 → 2.18.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.
Files changed (111) hide show
  1. package/.claude/commands/wogi-audit.md +212 -17
  2. package/.claude/commands/wogi-research.md +37 -0
  3. package/.claude/commands/wogi-review.md +200 -22
  4. package/.claude/commands/wogi-start.md +45 -0
  5. package/.claude/docs/claude-code-compatibility.md +46 -1
  6. package/.claude/docs/intent-grounded-review.md +209 -0
  7. package/.claude/settings.json +34 -1
  8. package/.workflow/agents/logic-adversary.md +8 -0
  9. package/.workflow/templates/claude-md.hbs +18 -0
  10. package/lib/installer.js +22 -0
  11. package/lib/utils.js +29 -3
  12. package/lib/workspace-changelog.js +2 -1
  13. package/lib/workspace-channel-server.js +4 -6
  14. package/lib/workspace-contracts.js +5 -4
  15. package/lib/workspace-events.js +8 -7
  16. package/lib/workspace-gates.js +4 -3
  17. package/lib/workspace-integration-tests.js +2 -1
  18. package/lib/workspace-intelligence.js +3 -2
  19. package/lib/workspace-locks.js +2 -1
  20. package/lib/workspace-messages.js +7 -6
  21. package/lib/workspace-routing.js +14 -26
  22. package/lib/workspace-session.js +7 -6
  23. package/lib/workspace-sync.js +9 -8
  24. package/package.json +4 -2
  25. package/scripts/base-workflow-step.js +1 -1
  26. package/scripts/flow +19 -0
  27. package/scripts/flow-adaptive-learning.js +1 -1
  28. package/scripts/flow-aggregate.js +2 -1
  29. package/scripts/flow-architect-pass.js +3 -3
  30. package/scripts/flow-archive-runs.js +372 -0
  31. package/scripts/flow-ask.js +1 -1
  32. package/scripts/flow-ast-grep.js +216 -0
  33. package/scripts/flow-audit-gates.js +1 -1
  34. package/scripts/flow-auto-learn.js +8 -11
  35. package/scripts/flow-bug.js +2 -2
  36. package/scripts/flow-capture-gate.js +644 -0
  37. package/scripts/flow-capture.js +4 -3
  38. package/scripts/flow-cli-flags.js +95 -0
  39. package/scripts/flow-community-sync.js +2 -1
  40. package/scripts/flow-community.js +6 -6
  41. package/scripts/flow-conclusion-classifier.js +310 -0
  42. package/scripts/flow-config-defaults.js +3 -3
  43. package/scripts/flow-constants.js +8 -11
  44. package/scripts/flow-context-scoring.js +1 -0
  45. package/scripts/flow-correction-detector.js +344 -3
  46. package/scripts/flow-damage-control.js +1 -1
  47. package/scripts/flow-decisions-merge.js +1 -0
  48. package/scripts/flow-done-gates.js +20 -0
  49. package/scripts/flow-done-report.js +2 -2
  50. package/scripts/flow-done.js +4 -4
  51. package/scripts/flow-epics.js +5 -11
  52. package/scripts/flow-health.js +145 -1
  53. package/scripts/flow-id.js +92 -0
  54. package/scripts/flow-io.js +15 -5
  55. package/scripts/flow-knowledge-router.js +2 -1
  56. package/scripts/flow-links.js +1 -1
  57. package/scripts/flow-log-manager.js +2 -1
  58. package/scripts/flow-logic-adversary.js +4 -4
  59. package/scripts/flow-long-input-cli.js +6 -0
  60. package/scripts/flow-long-input-stories.js +1 -1
  61. package/scripts/flow-loop-retry-learning.js +1 -1
  62. package/scripts/flow-mcp-capabilities.js +2 -3
  63. package/scripts/flow-mcp-docs.js +2 -1
  64. package/scripts/flow-memory-blocks.js +2 -1
  65. package/scripts/flow-memory-sync.js +1 -1
  66. package/scripts/flow-memory.js +767 -0
  67. package/scripts/flow-migrate-igr.js +1 -1
  68. package/scripts/flow-migrate.js +2 -1
  69. package/scripts/flow-model-adapter.js +1 -1
  70. package/scripts/flow-model-config.js +5 -1
  71. package/scripts/flow-model-profile.js +2 -1
  72. package/scripts/flow-orchestrate.js +3 -3
  73. package/scripts/flow-output.js +29 -0
  74. package/scripts/flow-parallel.js +10 -9
  75. package/scripts/flow-pattern-enforcer.js +2 -1
  76. package/scripts/flow-permissions-audit.js +124 -0
  77. package/scripts/flow-plugin-registry.js +2 -2
  78. package/scripts/flow-progress.js +5 -1
  79. package/scripts/flow-project-analyzer.js +1 -1
  80. package/scripts/flow-promote.js +510 -0
  81. package/scripts/flow-registries.js +86 -0
  82. package/scripts/flow-request-log.js +133 -0
  83. package/scripts/flow-research-protocol.js +0 -1
  84. package/scripts/flow-revision-tracker.js +2 -1
  85. package/scripts/flow-roadmap.js +2 -1
  86. package/scripts/flow-rules-sync.js +3 -7
  87. package/scripts/flow-session-end.js +3 -1
  88. package/scripts/flow-session-learning.js +6 -13
  89. package/scripts/flow-session-state.js +2 -2
  90. package/scripts/flow-setup-hooks.js +2 -1
  91. package/scripts/flow-skill-create.js +1 -1
  92. package/scripts/flow-skill-freshness.js +6 -7
  93. package/scripts/flow-skill-learn.js +1 -1
  94. package/scripts/flow-step-coverage.js +1 -1
  95. package/scripts/flow-step-security.js +1 -1
  96. package/scripts/flow-story.js +58 -10
  97. package/scripts/flow-sys.js +204 -0
  98. package/scripts/flow-task-hierarchy.js +88 -0
  99. package/scripts/flow-tech-debt.js +2 -1
  100. package/scripts/flow-test-api.js +1 -1
  101. package/scripts/flow-utils.js +60 -890
  102. package/scripts/hooks/core/bugfix-scope-gate.js +5 -4
  103. package/scripts/hooks/core/deploy-gate.js +1 -1
  104. package/scripts/hooks/core/pre-tool-helpers.js +72 -0
  105. package/scripts/hooks/core/pre-tool-orchestrator.js +442 -0
  106. package/scripts/hooks/core/routing-gate.js +8 -0
  107. package/scripts/hooks/core/session-end.js +28 -0
  108. package/scripts/hooks/entry/claude-code/pre-tool-use.js +48 -492
  109. package/scripts/hooks/entry/shared/hook-runner.js +1 -1
  110. package/scripts/registries/schema-registry.js +1 -1
  111. package/scripts/registries/service-registry.js +1 -1
@@ -50,7 +50,7 @@ const REQUIRED_IGR_SCRIPTS = [
50
50
  ];
51
51
 
52
52
  const REQUIRED_PERSONAS = ['logic-adversary.md', 'architect.md'];
53
- const REQUIRED_RUBRICS = ['logic-constitution-v1.md', 'logic-constitution-v2.md'];
53
+ const REQUIRED_RUBRICS = ['logic-constitution-v1.md', 'logic-constitution-v2.md', 'logic-constitution-v3.md'];
54
54
 
55
55
  // ARCH-002 fix (2026-04-13): use shared parseArgs from flow-cli-utils
56
56
  const { parseArgs } = require('./flow-cli-utils');
@@ -23,7 +23,8 @@ const {
23
23
  parseFlags,
24
24
  outputJson,
25
25
  checkSpecMigration,
26
- SPEC_FILE_MAP
26
+ SPEC_FILE_MAP,
27
+ getTodayDate
27
28
  } = require('./flow-utils')
28
29
  const { printHeader, printSection, color, success, warn, error, info } = require('./flow-output');;
29
30
 
@@ -27,7 +27,7 @@
27
27
 
28
28
  const fs = require('node:fs');
29
29
  const path = require('node:path');
30
- const { getProjectRoot, getConfig, PATHS, colors, readJson } = require('./flow-utils');
30
+ const { getProjectRoot, getConfig, PATHS, colors, readJson, getTodayDate } = require('./flow-utils');
31
31
 
32
32
  const ADAPTERS_DIR = PATHS.modelAdapters;
33
33
  const MODEL_STATS_PATH = PATHS.modelStatsLegacy;
@@ -113,7 +113,11 @@ function writeConfig(config) {
113
113
  * @returns {Object} Models config or empty structure
114
114
  */
115
115
  function getModelsConfig() {
116
- const config = readConfig();
116
+ // Use canonical getConfig() for reads — readConfig() is kept only for
117
+ // read-modify-write paths (updateModelsConfig, migrateOldConfig) where a
118
+ // cached reference would cause mutation bugs. (wf-7072d3ac / dup-006)
119
+ const { getConfig } = require('./flow-config-loader');
120
+ const config = getConfig();
117
121
  return config.models || { providers: {}, defaults: {} };
118
122
  }
119
123
 
@@ -39,7 +39,8 @@ const {
39
39
  error,
40
40
  parseFlags,
41
41
  outputJson,
42
- safeJsonParse
42
+ safeJsonParse,
43
+ getTodayDate
43
44
  } = require('./flow-utils');
44
45
 
45
46
  const { getSectionsByPins, readIndex } = require('./flow-section-index');
@@ -587,7 +587,7 @@ function logTokenMetrics(plan, executionResult, complexity) {
587
587
  const metricsPath = path.join(PATHS.state, 'hybrid-metrics.json');
588
588
 
589
589
  // Load existing metrics or create new array
590
- const metrics = readJson(metricsPath, []);
590
+ let metrics = readJson(metricsPath, []);
591
591
 
592
592
  // Add new metric entry
593
593
  const entry = {
@@ -1436,7 +1436,7 @@ class Orchestrator {
1436
1436
  taskType: result.taskType || taskType,
1437
1437
  success: true
1438
1438
  });
1439
- } catch (profileErr) {
1439
+ } catch (_err) {
1440
1440
  // Non-critical, continue
1441
1441
  }
1442
1442
 
@@ -1510,7 +1510,7 @@ class Orchestrator {
1510
1510
  log('dim', ` 📚 Applied learning from failure: ${failureLearning.learning?.category || 'unknown'}`);
1511
1511
  continue; // Skip default refinement, use learning-based enhancement
1512
1512
  }
1513
- } catch (learnErr) {
1513
+ } catch (_err) {
1514
1514
  // Non-critical, fall back to standard refinement
1515
1515
  }
1516
1516
 
@@ -182,6 +182,34 @@ function escapeRegex(str) {
182
182
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
183
183
  }
184
184
 
185
+ /**
186
+ * Canonical slugify — consolidates 4 duplicate impls across scripts/ per audit
187
+ * dup-003 (wf-7072d3ac, 2026-04-15).
188
+ *
189
+ * @param {string} str
190
+ * @param {Object} [opts]
191
+ * @param {'alnum'|'word'} [opts.mode='alnum']
192
+ * 'alnum' — strips everything except [a-z0-9]+ (2026 matches old auto-learn/session-learning/rules-sync behavior).
193
+ * 'word' — preserves word chars incl. underscores (old flow-story behavior).
194
+ * @param {number} [opts.maxLength] — truncate to this many chars (unset = unbounded).
195
+ * @returns {string}
196
+ */
197
+ function slugify(str, opts = {}) {
198
+ const { mode = 'alnum', maxLength } = opts;
199
+ if (!str || typeof str !== 'string') return '';
200
+ let s = str.toLowerCase().trim();
201
+ if (mode === 'word') {
202
+ s = s
203
+ .replace(/[^\w\s-]/g, '') // strip non-word chars except space/hyphen
204
+ .replace(/[\s_]+/g, '-'); // space/underscore → hyphen
205
+ } else {
206
+ s = s.replace(/[^a-z0-9]+/g, '-'); // single pass: anything non-alnum → hyphen
207
+ }
208
+ s = s.replace(/-+/g, '-').replace(/^-+|-+$/g, '');
209
+ if (Number.isFinite(maxLength) && maxLength > 0) s = s.slice(0, maxLength);
210
+ return s;
211
+ }
212
+
185
213
  module.exports = {
186
214
  colors,
187
215
  color,
@@ -195,4 +223,5 @@ module.exports = {
195
223
  showHelp,
196
224
  escapeRegex,
197
225
  getTodayDate,
226
+ slugify,
198
227
  };
@@ -281,9 +281,9 @@ async function executeParallel(tasks, executor, options = {}) {
281
281
  try {
282
282
  if (tracker) tracker.start(task.id);
283
283
  if (onStart) onStart(task);
284
- } catch (callbackError) {
284
+ } catch (err) {
285
285
  // Don't let callback errors prevent task execution
286
- console.warn(`Callback error for ${task.id}: ${callbackError.message}`);
286
+ console.warn(`Callback error for ${task.id}: ${err.message}`);
287
287
  }
288
288
 
289
289
  try {
@@ -294,22 +294,23 @@ async function executeParallel(tasks, executor, options = {}) {
294
294
  try {
295
295
  if (tracker) tracker.complete(task.id, result);
296
296
  if (onComplete) onComplete(task, result);
297
- } catch (callbackError) {
298
- console.warn(`Completion callback error for ${task.id}: ${callbackError.message}`);
297
+ } catch (err) {
298
+ console.warn(`Completion callback error for ${task.id}: ${err.message}`);
299
299
  }
300
300
 
301
301
  return { taskId: task.id, success: true, result };
302
302
  } catch (err) {
303
303
  finished.add(task.id); // Mark as finished but NOT succeeded
304
304
 
305
+ const outerErr = err;
305
306
  try {
306
- if (tracker) tracker.fail(task.id, err);
307
- if (onError) onError(task, err);
308
- } catch (callbackError) {
309
- console.warn(`Error callback error for ${task.id}: ${callbackError.message}`);
307
+ if (tracker) tracker.fail(task.id, outerErr);
308
+ if (onError) onError(task, outerErr);
309
+ } catch (err) {
310
+ console.warn(`Error callback error for ${task.id}: ${err.message}`);
310
311
  }
311
312
 
312
- return { taskId: task.id, success: false, error: err.message };
313
+ return { taskId: task.id, success: false, error: outerErr.message };
313
314
  }
314
315
  });
315
316
 
@@ -32,7 +32,8 @@ const {
32
32
  info,
33
33
  success,
34
34
  warn,
35
- error
35
+ error,
36
+ getTodayDate
36
37
  } = require('./flow-utils');
37
38
 
38
39
  // ============================================================
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Permission Rule Audit (Claude Code settings.local.json)
5
+ *
6
+ * Analyzes Claude Code permission rules (e.g., 'Bash(git:*)') for duplicates,
7
+ * overly broad wildcards, and redundant specific rules covered by wildcards.
8
+ * Extracted from flow-utils.js (wf-94cc3b72 epic — decomposition pass).
9
+ *
10
+ * Separate from flow-permissions.js (which manages user permission grants
11
+ * for WogiFlow operations) — these audit the upstream Claude Code config.
12
+ *
13
+ * Pure functions with no filesystem side effects. Zero external deps.
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ /**
19
+ * Analyze permission rules for issues
20
+ * @param {string[]} permissions - Array of permission rule strings
21
+ * @returns {Object} Analysis result with duplicates, overbroad, shadowed, total
22
+ */
23
+ function analyzePermissions(permissions) {
24
+ const result = {
25
+ duplicates: [],
26
+ overbroad: [],
27
+ shadowed: [],
28
+ total: permissions.length
29
+ };
30
+
31
+ // Check for duplicates
32
+ const seen = new Set();
33
+ for (const perm of permissions) {
34
+ if (seen.has(perm)) {
35
+ result.duplicates.push(perm);
36
+ }
37
+ seen.add(perm);
38
+ }
39
+
40
+ // Check for overly broad patterns (wildcards on core tools)
41
+ const overbroadPatterns = ['Bash(*)', 'Edit(*)', 'Write(*)', 'Read(*)'];
42
+ for (const perm of permissions) {
43
+ if (overbroadPatterns.includes(perm)) {
44
+ result.overbroad.push(perm);
45
+ }
46
+ }
47
+
48
+ // Check for shadowed rules (specific rules covered by wildcards)
49
+ const wildcards = permissions.filter(p => p.includes('*'));
50
+ const specific = permissions.filter(p => !p.includes('*'));
51
+
52
+ for (const spec of specific) {
53
+ const match = spec.match(/^(\w+)\((.+)\)$/);
54
+ if (match) {
55
+ const [, tool, pattern] = match;
56
+ for (const wild of wildcards) {
57
+ const wildMatch = wild.match(/^(\w+)\((.+)\)$/);
58
+ if (wildMatch && wildMatch[1] === tool) {
59
+ const wildPattern = wildMatch[2].replace(/\*/g, '.*');
60
+ try {
61
+ const regex = new RegExp(`^${wildPattern}$`);
62
+ if (regex.test(pattern)) {
63
+ result.shadowed.push({ specific: spec, wildcard: wild });
64
+ break;
65
+ }
66
+ } catch (_err) {
67
+ // Invalid regex, skip
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ return result;
75
+ }
76
+
77
+ /**
78
+ * Validate permission rules and return classified issues + warnings.
79
+ * @param {string[]} permissions - Array of permission rules
80
+ * @returns {Object} { valid, issues, warnings, analysis }
81
+ */
82
+ function validatePermissions(permissions) {
83
+ const analysis = analyzePermissions(permissions);
84
+
85
+ const issues = [];
86
+ const warnings = [];
87
+
88
+ if (analysis.duplicates.length > 0) {
89
+ warnings.push({
90
+ type: 'duplicate',
91
+ message: `${analysis.duplicates.length} duplicate rule(s) found`,
92
+ items: analysis.duplicates
93
+ });
94
+ }
95
+
96
+ if (analysis.overbroad.length > 0) {
97
+ issues.push({
98
+ type: 'overbroad',
99
+ severity: 'critical',
100
+ message: `${analysis.overbroad.length} overly broad rule(s) found`,
101
+ items: analysis.overbroad
102
+ });
103
+ }
104
+
105
+ if (analysis.shadowed.length > 0) {
106
+ warnings.push({
107
+ type: 'shadowed',
108
+ message: `${analysis.shadowed.length} rule(s) shadowed by wildcards (redundant)`,
109
+ items: analysis.shadowed.map(s => s.specific)
110
+ });
111
+ }
112
+
113
+ return {
114
+ valid: issues.length === 0,
115
+ issues,
116
+ warnings,
117
+ analysis
118
+ };
119
+ }
120
+
121
+ module.exports = {
122
+ analyzePermissions,
123
+ validatePermissions,
124
+ };
@@ -29,8 +29,8 @@ const {
29
29
  printHeader, PATHS
30
30
  } = require('./flow-utils');
31
31
 
32
- // Dangerous keys that must never be used as plugin names or metadata keys
33
- const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
32
+ // Local DANGEROUS_KEYS consolidated to flow-io canonical (wf-2f6fbb12 / dup-002).
33
+ const { DANGEROUS_KEYS } = require('./flow-io');
34
34
 
35
35
  // Allowlists for capability fields
36
36
  const VALID_MODES = new Set(['standalone', 'flow-integrated', 'trigger']);
@@ -7,7 +7,11 @@
7
7
  */
8
8
 
9
9
  const readline = require('node:readline/promises');
10
- const { colors } = require('./flow-utils');
10
+ const { colors, getTodayDate } = require('./flow-utils');
11
+
12
+ // ANSI escape prefix (was referenced as ESC without definition — latent
13
+ // no-undef bug caught by eslint upgrade; wf-5a6df88a)
14
+ const ESC = '\x1b';
11
15
 
12
16
  const symbols = {
13
17
  success: '✅',
@@ -12,7 +12,7 @@
12
12
 
13
13
  const fs = require('node:fs');
14
14
  const path = require('node:path');
15
- const { getProjectRoot, safeJsonParse, getConfig } = require('./flow-utils');
15
+ const { getProjectRoot, safeJsonParse, getConfig, getTodayDate } = require('./flow-utils');
16
16
 
17
17
  const PROJECT_ROOT = process.argv[2] || getProjectRoot();
18
18