wogiflow 2.4.2 → 2.4.3
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.
- package/.claude/docs/claude-code-compatibility.md +27 -0
- package/.claude/settings.json +1 -1
- package/.workflow/models/registry.json +1 -1
- package/package.json +4 -4
- package/scripts/MEMORY-ARCHITECTURE.md +1 -1
- package/scripts/base-workflow-step.js +136 -0
- package/scripts/flow-adaptive-learning.js +8 -9
- package/scripts/flow-aggregate.js +11 -6
- package/scripts/flow-api-index.js +4 -6
- package/scripts/flow-assumption-detector.js +0 -2
- package/scripts/flow-audit.js +15 -2
- package/scripts/flow-auto-context.js +8 -12
- package/scripts/flow-auto-learn.js +49 -49
- package/scripts/flow-background.js +5 -6
- package/scripts/flow-bridge-state.js +8 -10
- package/scripts/flow-bulk-loop.js +1 -3
- package/scripts/flow-bulk-orchestrator.js +1 -3
- package/scripts/flow-cascade-completion.js +0 -2
- package/scripts/flow-cascade.js +4 -4
- package/scripts/flow-checkpoint.js +10 -13
- package/scripts/flow-code-intelligence.js +10 -12
- package/scripts/flow-community-sync.js +4 -4
- package/scripts/flow-community.js +12 -20
- package/scripts/flow-config-defaults.js +0 -2
- package/scripts/flow-config-interactive.js +9 -5
- package/scripts/flow-config-loader.js +49 -92
- package/scripts/flow-config-substitution.js +0 -2
- package/scripts/flow-context-estimator.js +4 -4
- package/scripts/flow-context-init.js +10 -12
- package/scripts/flow-context-manager.js +0 -2
- package/scripts/flow-context-scoring.js +2 -2
- package/scripts/flow-contract-scan.js +6 -9
- package/scripts/flow-correct.js +29 -27
- package/scripts/flow-correction-detector.js +5 -1
- package/scripts/flow-damage-control.js +47 -54
- package/scripts/flow-decisions-merge.js +4 -14
- package/scripts/flow-diff.js +5 -8
- package/scripts/flow-done-gates.js +786 -0
- package/scripts/flow-done-report.js +123 -0
- package/scripts/flow-done.js +71 -717
- package/scripts/flow-entropy-monitor.js +1 -3
- package/scripts/flow-eval.js +5 -5
- package/scripts/flow-extraction-review.js +1 -0
- package/scripts/flow-failure-categories.js +0 -2
- package/scripts/flow-figma-confirm.js +5 -9
- package/scripts/flow-figma-generate.js +8 -10
- package/scripts/flow-figma-index.js +8 -10
- package/scripts/flow-figma-match.js +3 -5
- package/scripts/flow-figma-mcp-server.js +2 -4
- package/scripts/flow-figma-orchestrator.js +2 -3
- package/scripts/flow-figma-registry.js +2 -3
- package/scripts/flow-framework-resolver.js +0 -2
- package/scripts/flow-function-index.js +4 -6
- package/scripts/flow-gate-confidence.js +2 -2
- package/scripts/flow-gitignore.js +0 -2
- package/scripts/flow-guided-edit.js +5 -6
- package/scripts/flow-health.js +5 -6
- package/scripts/flow-hook-errors.js +6 -0
- package/scripts/flow-hook-status.js +263 -0
- package/scripts/flow-hooks.js +17 -29
- package/scripts/flow-http-client.js +9 -8
- package/scripts/flow-hybrid-interactive.js +7 -12
- package/scripts/flow-hybrid-test.js +12 -13
- package/scripts/flow-instruction-richness.js +1 -1
- package/scripts/flow-io.js +21 -4
- package/scripts/flow-knowledge-router.js +9 -3
- package/scripts/flow-learning-orchestrator.js +318 -13
- package/scripts/flow-links.js +5 -7
- package/scripts/flow-long-input-association.js +275 -0
- package/scripts/flow-long-input-chunking.js +1 -0
- package/scripts/flow-long-input-cli.js +0 -2
- package/scripts/flow-long-input-complexity.js +0 -2
- package/scripts/flow-long-input-constants.js +0 -2
- package/scripts/flow-long-input-contradictions.js +351 -0
- package/scripts/flow-long-input-detection.js +0 -2
- package/scripts/flow-long-input-passes.js +885 -0
- package/scripts/flow-long-input-stories.js +1 -1
- package/scripts/flow-long-input-voice.js +0 -2
- package/scripts/flow-long-input.js +425 -3005
- package/scripts/flow-loop-retry-learning.js +2 -3
- package/scripts/flow-lsp.js +3 -3
- package/scripts/flow-mcp-docs.js +3 -4
- package/scripts/flow-memory-db.js +6 -8
- package/scripts/flow-memory-sync.js +18 -11
- package/scripts/flow-metrics.js +1 -2
- package/scripts/flow-model-adapter.js +2 -3
- package/scripts/flow-model-config.js +72 -104
- package/scripts/flow-model-router.js +2 -2
- package/scripts/flow-model-types.js +0 -2
- package/scripts/flow-multi-approach.js +5 -6
- package/scripts/flow-orchestrate-context.js +3 -7
- package/scripts/flow-orchestrate-rollback.js +3 -8
- package/scripts/flow-orchestrate-state.js +8 -14
- package/scripts/flow-orchestrate-templates.js +2 -6
- package/scripts/flow-orchestrate-validator.js +5 -9
- package/scripts/flow-orchestrate.js +126 -103
- package/scripts/flow-output.js +0 -2
- package/scripts/flow-parallel.js +1 -1
- package/scripts/flow-paths.js +23 -2
- package/scripts/flow-pattern-enforcer.js +30 -28
- package/scripts/flow-pattern-extractor.js +3 -4
- package/scripts/flow-pending.js +0 -2
- package/scripts/flow-permissions.js +2 -3
- package/scripts/flow-plugin-registry.js +10 -12
- package/scripts/flow-prd-manager.js +1 -1
- package/scripts/flow-progress.js +7 -9
- package/scripts/flow-prompt-composer.js +3 -3
- package/scripts/flow-prompt-template.js +2 -2
- package/scripts/flow-providers.js +7 -4
- package/scripts/flow-registry-manager.js +7 -12
- package/scripts/flow-regression.js +9 -11
- package/scripts/flow-roadmap.js +2 -2
- package/scripts/flow-run-trace.js +16 -15
- package/scripts/flow-safety.js +2 -5
- package/scripts/flow-scanner-base.js +5 -7
- package/scripts/flow-scenario-engine.js +1 -5
- package/scripts/flow-security.js +29 -0
- package/scripts/flow-session-end.js +32 -41
- package/scripts/flow-session-learning.js +53 -49
- package/scripts/flow-setup-hooks.js +2 -3
- package/scripts/flow-skill-create.js +7 -12
- package/scripts/flow-skill-generator.js +12 -16
- package/scripts/flow-skill-learn.js +17 -8
- package/scripts/flow-skill-matcher.js +1 -2
- package/scripts/flow-spec-generator.js +2 -4
- package/scripts/flow-stack-wizard.js +5 -7
- package/scripts/flow-standards-learner.js +35 -16
- package/scripts/flow-start.js +2 -0
- package/scripts/flow-stats-collector.js +2 -2
- package/scripts/flow-status.js +10 -10
- package/scripts/flow-statusline-setup.js +2 -2
- package/scripts/flow-step-changelog.js +2 -3
- package/scripts/flow-step-comments.js +66 -81
- package/scripts/flow-step-complexity.js +50 -70
- package/scripts/flow-step-coverage.js +3 -5
- package/scripts/flow-step-knowledge.js +2 -3
- package/scripts/flow-step-pr-tests.js +64 -74
- package/scripts/flow-step-regression.js +3 -5
- package/scripts/flow-step-review.js +86 -103
- package/scripts/flow-step-security.js +111 -121
- package/scripts/flow-step-silent-failures.js +56 -83
- package/scripts/flow-step-simplifier.js +52 -70
- package/scripts/flow-story.js +4 -7
- package/scripts/flow-strict-adherence.js +3 -4
- package/scripts/flow-task-checkpoint.js +36 -5
- package/scripts/flow-task-enforcer.js +2 -24
- package/scripts/flow-tech-debt.js +1 -1
- package/scripts/flow-template-extractor.js +1 -0
- package/scripts/flow-templates.js +11 -13
- package/scripts/flow-test-api.js +9 -13
- package/scripts/flow-test-discovery.js +1 -1
- package/scripts/flow-test-generate.js +5 -9
- package/scripts/flow-test-integrity.js +3 -7
- package/scripts/flow-test-ui.js +5 -9
- package/scripts/flow-testing-deps.js +1 -3
- package/scripts/flow-tiered-learning.js +4 -4
- package/scripts/flow-todowrite-sync.js +1 -1
- package/scripts/flow-tokens.js +0 -2
- package/scripts/flow-verification-profile.js +6 -10
- package/scripts/flow-verify.js +12 -16
- package/scripts/flow-version-check.js +4 -12
- package/scripts/flow-webmcp-generator.js +3 -5
- package/scripts/flow-workflow-steps.js +0 -2
- package/scripts/flow-workflow.js +9 -11
- package/scripts/hooks/adapters/claude-code.js +2 -0
- package/scripts/hooks/core/config-change.js +1 -0
- package/scripts/hooks/core/extension-registry.js +0 -2
- package/scripts/hooks/core/instructions-loaded.js +1 -1
- package/scripts/hooks/core/observation-capture.js +5 -5
- package/scripts/hooks/core/phase-gate.js +5 -0
- package/scripts/hooks/core/post-compact.js +1 -12
- package/scripts/hooks/core/research-gate.js +2 -12
- package/scripts/hooks/core/routing-gate.js +6 -0
- package/scripts/hooks/core/task-completed.js +12 -0
- package/scripts/hooks/core/worktree-lifecycle.js +1 -1
- package/scripts/hooks/entry/claude-code/config-change.js +6 -29
- package/scripts/hooks/entry/claude-code/instructions-loaded.js +5 -30
- package/scripts/hooks/entry/claude-code/post-compact.js +4 -31
- package/scripts/hooks/entry/claude-code/post-tool-use.js +121 -172
- package/scripts/hooks/entry/claude-code/pre-tool-use.js +260 -361
- package/scripts/hooks/entry/claude-code/session-end.js +4 -28
- package/scripts/hooks/entry/claude-code/session-start.js +205 -243
- package/scripts/hooks/entry/claude-code/setup.js +8 -49
- package/scripts/hooks/entry/claude-code/stop.js +40 -72
- package/scripts/hooks/entry/claude-code/task-completed.js +4 -28
- package/scripts/hooks/entry/claude-code/user-prompt-submit.js +113 -195
- package/scripts/hooks/entry/claude-code/worktree-create.js +6 -25
- package/scripts/hooks/entry/claude-code/worktree-remove.js +6 -25
- package/scripts/hooks/entry/shared/hook-runner.js +99 -0
- package/scripts/hooks/entry/shared/read-stdin.js +0 -2
- package/scripts/registries/api-registry.js +0 -2
- package/scripts/registries/component-registry.js +5 -9
- package/scripts/registries/contract-scanner.js +2 -9
- package/scripts/registries/function-registry.js +0 -2
- package/scripts/registries/schema-registry.js +14 -18
- package/scripts/registries/service-registry.js +23 -27
|
@@ -70,6 +70,7 @@ flow parallel check # See available parallel tasks
|
|
|
70
70
|
| 1.9.5+ | 2.1.74+ | SessionEnd timeout fix, managed policy ask rules, autoMemoryDirectory, Agent tool routing gate fix |
|
|
71
71
|
| 2.0.0+ | 2.1.76+ | PostCompact hook, Elicitation/ElicitationResult events, deferred tool schema fix |
|
|
72
72
|
| 2.1.0+ | 2.1.77+ | PreToolUse allow/deny separation, 128k output tokens, worktree sparse checkout, compaction circuit breaker |
|
|
73
|
+
| 2.4.0+ | 2.1.83+ | managed-settings.d/, CwdChanged/FileChanged hooks, ENV_SCRUB, --channels limitations, MEMORY.md 25KB cap |
|
|
73
74
|
|
|
74
75
|
### Environment Variables (2.1.19+)
|
|
75
76
|
|
|
@@ -236,6 +237,32 @@ await cancelTask('wf-123', 'superseded', false);
|
|
|
236
237
|
- **Memory growth fix**: Fixed progress messages surviving compaction in long-running sessions. Reduces memory pressure during long WogiFlow bulk-loop sessions.
|
|
237
238
|
- **Faster startup on macOS**: ~60ms faster by reading keychain credentials in parallel. Faster `--resume` on fork-heavy sessions — up to 45% faster loading and ~100-150MB less peak memory. Benefits WogiFlow sessions with heavy hook context.
|
|
238
239
|
|
|
240
|
+
### Features in 2.1.83+
|
|
241
|
+
|
|
242
|
+
- **managed-settings.d/ drop-in directory**: A `managed-settings.d/` directory alongside `managed-settings.json` allows separate teams/tools to deploy independent policy fragments that merge alphabetically. WogiFlow currently generates `settings.local.json` — for wogiflow-cloud teams, this opens the door to deploying team policies as individual fragments (e.g., `00-wogiflow-hooks.json`, `50-team-policy.json`). No code change needed yet; tracked as cloud opportunity.
|
|
243
|
+
|
|
244
|
+
- **CwdChanged and FileChanged hook events**: Two new hook events. `CwdChanged` fires when the working directory changes (useful for direnv-style setups). `FileChanged` fires when watched files change on disk — WogiFlow could use this to detect external changes to `.workflow/state/` files and auto-rescan. Added to `UNUSED_SUPPORTED_EVENTS` in `claude-code.js`. Implementation deferred to a future task.
|
|
245
|
+
|
|
246
|
+
- **CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1**: Strips Anthropic and cloud provider credentials from subprocess environments (Bash tool, hooks, MCP stdio servers). **Impact on WogiFlow**: Hooks are subprocesses, so any hook that needs API keys (e.g., `flow-correction-detector.js` spawning a child process for async correction detection) will not have credentials available. The correction detector now gracefully degrades (returns `isCorrection: false`) when no API key is available. `flow-providers.js` makes direct `https.request()` calls from the hook process itself (not a subprocess of the subprocess), so it reads `process.env` before scrubbing — but if ENV_SCRUB applies transitively to hook processes, provider calls would also be affected. For wogiflow-cloud: if cloud sync hooks need API keys, they must use an alternative credential mechanism (file-based, keychain, or passed via hook input JSON).
|
|
247
|
+
|
|
248
|
+
- **Agents can declare initialPrompt in frontmatter**: Agents can now auto-submit a first turn without the AI composing it. WogiFlow's 11 persona agents in `agents/` could use this for standardized opening probes. No code change needed; optimization opportunity.
|
|
249
|
+
|
|
250
|
+
- **Background subagent fixes**: (1) Fixed subagents becoming invisible after context compaction — this prevented duplicate agent spawns in WogiFlow's parallel explore phase. (2) Fixed agents staying stuck in "running" state when git/API calls hang during cleanup. Both fixes improve reliability of `/wogi-start` explore phase and `/wogi-bulk-loop`.
|
|
251
|
+
|
|
252
|
+
- **--channels disables AskUserQuestion and plan mode**: When `--channels` is active (remote/SDK), `AskUserQuestion` and plan-mode tools are disabled. **Impact on WogiFlow**: WogiFlow uses `AskUserQuestion` extensively for approval gates, clarifying questions, and interactive decisions. In `--channels` mode, these will silently fail or be unavailable. WogiFlow should detect channels mode and fall back to non-interactive patterns: auto-approve with defaults, skip clarifying questions, use best-effort decisions. Documented in CLAUDE.md template and wogi-start command.
|
|
253
|
+
|
|
254
|
+
- **TaskOutput deprecated**: `TaskOutput` tool is deprecated in favor of using `Read` on the background task's output file path. WogiFlow does not use `TaskOutput` directly (confirmed by codebase search). No change needed.
|
|
255
|
+
|
|
256
|
+
- **MEMORY.md index truncation**: Now truncates at **25KB** as well as 200 lines (previously only 200 lines). WogiFlow's MEMORY.md enforcement block at the top consumes space from this budget. Projects with large MEMORY.md files may lose entries silently. The CLAUDE.md template's auto-memory section already mentions 200 lines; the 25KB limit is enforced by Claude Code's system prompt and does not need to be duplicated in the template.
|
|
257
|
+
|
|
258
|
+
- **Plugin manifest.userConfig**: Plugins can now prompt for configuration at enable time, with `sensitive: true` values stored in keychain (macOS) or protected credentials file. If WogiFlow becomes a Claude Code plugin, this provides native credential storage for cloud API tokens and model API keys — replacing `wogi login`'s file-based token storage. Tracked as cloud opportunity.
|
|
259
|
+
|
|
260
|
+
- **WebFetch identifies as Claude-User**: `WebFetch` now sends a `Claude-User` user agent so site operators can recognize and allowlist/block Claude Code traffic via `robots.txt`. WogiFlow's explore agents (Agent 2: Best Practices, Agent 3: Version Verifier) use `WebFetch` for research. If sites block `Claude-User`, research agents will get empty results. Agents should treat unexpectedly empty WebFetch results as potentially blocked and log a warning.
|
|
261
|
+
|
|
262
|
+
- **--mcp-config bypass fix**: Fixed `--mcp-config` CLI flag bypassing `allowedMcpServers`/`deniedMcpServers` managed policy enforcement. Security improvement — no WogiFlow code change needed.
|
|
263
|
+
|
|
264
|
+
- **Uninstalled plugin hooks fix**: Fixed uninstalled plugin hooks continuing to fire until the next session. Improves hook hygiene for WogiFlow plugin management.
|
|
265
|
+
|
|
239
266
|
### Simple Mode Naming Distinction
|
|
240
267
|
|
|
241
268
|
Claude Code's `CLAUDE_CODE_SIMPLE` environment variable (which enables a simplified tool set) is **unrelated** to WogiFlow's `loops.simpleMode` (a lightweight task completion loop using string detection). They are separate features that happen to share the word "simple":
|
package/.claude/settings.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wogiflow",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.3",
|
|
4
4
|
"description": "AI-powered development workflow management system with multi-model support",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -61,12 +61,12 @@
|
|
|
61
61
|
"sql.js": "^1.14.1"
|
|
62
62
|
},
|
|
63
63
|
"optionalDependencies": {
|
|
64
|
-
"@babel/parser": "^7.29.
|
|
64
|
+
"@babel/parser": "^7.29.2",
|
|
65
65
|
"@babel/traverse": "^7.29.0",
|
|
66
|
-
"@
|
|
66
|
+
"@huggingface/transformers": "^3.0.0"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"eslint": "^
|
|
69
|
+
"eslint": "^10.0.0"
|
|
70
70
|
},
|
|
71
71
|
"engines": {
|
|
72
72
|
"node": ">=18.0.0"
|
|
@@ -88,7 +88,7 @@ flow knowledge-sync regenerate # Regenerate stale files
|
|
|
88
88
|
|
|
89
89
|
**Features**:
|
|
90
90
|
- SQLite database using sql.js (pure JS)
|
|
91
|
-
- Embedding generation via @
|
|
91
|
+
- Embedding generation via @huggingface/transformers
|
|
92
92
|
- Semantic similarity search
|
|
93
93
|
- Facts, proposals, and PRD chunk storage
|
|
94
94
|
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wogi Flow - BaseWorkflowStep
|
|
3
|
+
*
|
|
4
|
+
* Base class for all flow-step-*.js workflow steps.
|
|
5
|
+
* Provides shared infrastructure: file filtering, context loading,
|
|
6
|
+
* validation, and result formatting.
|
|
7
|
+
*
|
|
8
|
+
* Subclasses override execute() with step-specific logic.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* const { BaseWorkflowStep } = require('./base-workflow-step');
|
|
12
|
+
*
|
|
13
|
+
* class MyStep extends BaseWorkflowStep {
|
|
14
|
+
* constructor() {
|
|
15
|
+
* super('myStep', {
|
|
16
|
+
* extensions: ['.js', '.ts'],
|
|
17
|
+
* excludeTests: true,
|
|
18
|
+
* excludeDts: true,
|
|
19
|
+
* });
|
|
20
|
+
* }
|
|
21
|
+
* async execute(files, options) {
|
|
22
|
+
* // ... step-specific logic
|
|
23
|
+
* return this.pass('All checks passed');
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* module.exports = { run: (options) => new MyStep().run(options) };
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const fs = require('node:fs');
|
|
31
|
+
const path = require('node:path');
|
|
32
|
+
const { PATHS } = require('./flow-utils');
|
|
33
|
+
|
|
34
|
+
class BaseWorkflowStep {
|
|
35
|
+
/**
|
|
36
|
+
* @param {string} name - Step identifier
|
|
37
|
+
* @param {object} [filterOpts] - File filtering options
|
|
38
|
+
* @param {string[]} [filterOpts.extensions] - File extensions to include (e.g., ['.js', '.ts'])
|
|
39
|
+
* @param {boolean} [filterOpts.excludeTests=true] - Exclude .test./.spec. files
|
|
40
|
+
* @param {boolean} [filterOpts.excludeDts=true] - Exclude .d.ts files
|
|
41
|
+
*/
|
|
42
|
+
constructor(name, filterOpts = {}) {
|
|
43
|
+
this.name = name;
|
|
44
|
+
this.extensions = filterOpts.extensions ?? ['.js', '.ts', '.jsx', '.tsx'];
|
|
45
|
+
this.excludeTests = filterOpts.excludeTests ?? true;
|
|
46
|
+
this.excludeDts = filterOpts.excludeDts ?? true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Main entry point — matches the `run(options)` interface expected by flow-workflow-steps.js
|
|
51
|
+
* @param {object} options
|
|
52
|
+
* @param {string[]} [options.files] - Files modified
|
|
53
|
+
* @param {object} [options.stepConfig] - Step configuration
|
|
54
|
+
* @param {string} [options.mode] - Step mode (block/warn/prompt/auto)
|
|
55
|
+
* @param {string} [options.taskType] - Task type
|
|
56
|
+
* @param {string} [options.taskId] - Task ID
|
|
57
|
+
* @param {string} [options.taskTitle] - Task title
|
|
58
|
+
* @returns {Promise<{passed: boolean, message: string, details?: any}>}
|
|
59
|
+
*/
|
|
60
|
+
async run(options = {}) {
|
|
61
|
+
const { files = [], stepConfig = {}, ...rest } = options;
|
|
62
|
+
|
|
63
|
+
// Filter files to those this step cares about
|
|
64
|
+
const filteredFiles = this.filterFiles(files);
|
|
65
|
+
|
|
66
|
+
if (filteredFiles.length === 0) {
|
|
67
|
+
return this.pass(`No ${this.name}-eligible files modified`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Delegate to subclass
|
|
71
|
+
return await this.execute(filteredFiles, { stepConfig, ...rest });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Override in subclass — contains the step-specific logic.
|
|
76
|
+
* @param {string[]} files - Filtered files
|
|
77
|
+
* @param {object} options - Remaining options (stepConfig, mode, taskType, etc.)
|
|
78
|
+
* @returns {Promise<{passed: boolean, message: string, details?: any}>}
|
|
79
|
+
*/
|
|
80
|
+
async execute(files, options) {
|
|
81
|
+
throw new Error(`${this.name}: execute() must be overridden`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Filter files based on step's extension and exclusion rules.
|
|
86
|
+
* @param {string[]} files - Raw file list
|
|
87
|
+
* @returns {string[]} Filtered files
|
|
88
|
+
*/
|
|
89
|
+
filterFiles(files) {
|
|
90
|
+
return files.filter(f => {
|
|
91
|
+
const hasExt = this.extensions.some(ext => f.endsWith(ext));
|
|
92
|
+
if (!hasExt) return false;
|
|
93
|
+
if (this.excludeTests && (f.includes('.test.') || f.includes('.spec.'))) return false;
|
|
94
|
+
if (this.excludeDts && f.endsWith('.d.ts')) return false;
|
|
95
|
+
return true;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Read a file safely, returning null on error.
|
|
101
|
+
* @param {string} relativePath - Path relative to project root
|
|
102
|
+
* @returns {string|null} File content or null
|
|
103
|
+
*/
|
|
104
|
+
readFile(relativePath) {
|
|
105
|
+
const fullPath = path.join(PATHS.root, relativePath);
|
|
106
|
+
try {
|
|
107
|
+
if (!fs.existsSync(fullPath)) return null;
|
|
108
|
+
return fs.readFileSync(fullPath, 'utf8');
|
|
109
|
+
} catch (_err) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Create a passing result.
|
|
116
|
+
* @param {string} message
|
|
117
|
+
* @returns {{passed: true, message: string}}
|
|
118
|
+
*/
|
|
119
|
+
pass(message) {
|
|
120
|
+
return { passed: true, message };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Create a failing result.
|
|
125
|
+
* @param {string} message
|
|
126
|
+
* @param {any} [details]
|
|
127
|
+
* @returns {{passed: false, message: string, details?: any}}
|
|
128
|
+
*/
|
|
129
|
+
fail(message, details) {
|
|
130
|
+
const result = { passed: false, message };
|
|
131
|
+
if (details !== undefined) result.details = details;
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = { BaseWorkflowStep };
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
const fs = require('node:fs');
|
|
16
16
|
const path = require('node:path');
|
|
17
17
|
const { execFileSync } = require('node:child_process');
|
|
18
|
-
const { getProjectRoot, colors } = require('./flow-utils');
|
|
18
|
+
const { getProjectRoot, colors, PATHS } = require('./flow-utils');
|
|
19
19
|
const { error: errorMsg } = require('./flow-output');
|
|
20
20
|
const { readJson } = require('./flow-io');
|
|
21
21
|
const { storeSingleLearning, getAdapterPath } = require('./flow-model-adapter');
|
|
@@ -26,9 +26,8 @@ const {
|
|
|
26
26
|
} = require('./flow-failure-categories');
|
|
27
27
|
const { validateRepoFormat, safeGitCommand, sanitizeCommitMessage } = require('./flow-security');
|
|
28
28
|
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const STRATEGY_STATS_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'strategy-effectiveness.json');
|
|
29
|
+
const LEARNING_LOG_PATH = path.join(PATHS.state, 'adaptive-learning.json');
|
|
30
|
+
const STRATEGY_STATS_PATH = path.join(PATHS.state, 'strategy-effectiveness.json');
|
|
32
31
|
|
|
33
32
|
// ============================================================
|
|
34
33
|
// Failure Analysis
|
|
@@ -690,7 +689,7 @@ function exportLearningsForSharing() {
|
|
|
690
689
|
}
|
|
691
690
|
|
|
692
691
|
// Load model adapter files and extract learnings sections
|
|
693
|
-
const adaptersDir =
|
|
692
|
+
const adaptersDir = PATHS.modelAdapters;
|
|
694
693
|
if (fs.existsSync(adaptersDir)) {
|
|
695
694
|
const adapterFiles = fs.readdirSync(adaptersDir).filter(f => f.endsWith('.md') && !f.startsWith('_'));
|
|
696
695
|
|
|
@@ -845,7 +844,7 @@ async function contributeLearnings(upstreamRepo = 'your-org/wogi-flow', options
|
|
|
845
844
|
|
|
846
845
|
try {
|
|
847
846
|
// Create contribution file
|
|
848
|
-
const contributionDir = path.join(
|
|
847
|
+
const contributionDir = path.join(PATHS.workflow, 'contributions');
|
|
849
848
|
if (!fs.existsSync(contributionDir)) {
|
|
850
849
|
fs.mkdirSync(contributionDir, { recursive: true });
|
|
851
850
|
}
|
|
@@ -935,7 +934,7 @@ async function createAutoPR(upstreamRepo, options = {}) {
|
|
|
935
934
|
|
|
936
935
|
try {
|
|
937
936
|
// Clone fork, create branch, add files, push, create PR
|
|
938
|
-
const tempDir = path.join(
|
|
937
|
+
const tempDir = path.join(PATHS.workflow, 'temp-pr');
|
|
939
938
|
|
|
940
939
|
console.log(`${colors.cyan}Creating PR automatically...${colors.reset}`);
|
|
941
940
|
|
|
@@ -1136,12 +1135,12 @@ if (require.main === module) {
|
|
|
1136
1135
|
}
|
|
1137
1136
|
|
|
1138
1137
|
// Save export file
|
|
1139
|
-
const exportPath = path.join(
|
|
1138
|
+
const exportPath = path.join(PATHS.workflow, 'learnings-export.json');
|
|
1140
1139
|
fs.writeFileSync(exportPath, JSON.stringify(data, null, 2));
|
|
1141
1140
|
console.log(`\n${colors.green}✅ Exported to: ${exportPath}${colors.reset}`);
|
|
1142
1141
|
|
|
1143
1142
|
// Also create PR-ready markdown
|
|
1144
|
-
const prPath = path.join(
|
|
1143
|
+
const prPath = path.join(PATHS.workflow, 'learnings-contribution.md');
|
|
1145
1144
|
fs.writeFileSync(prPath, formatExportForPR(data));
|
|
1146
1145
|
console.log(`${colors.green}✅ PR-ready format: ${prPath}${colors.reset}`);
|
|
1147
1146
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
const fs = require('node:fs');
|
|
18
18
|
const path = require('node:path');
|
|
19
|
-
const readline = require('node:readline');
|
|
19
|
+
const readline = require('node:readline/promises');
|
|
20
20
|
const {
|
|
21
21
|
PATHS,
|
|
22
22
|
PROJECT_ROOT,
|
|
@@ -36,7 +36,7 @@ const { syncDecisionsToRules } = require('./flow-rules-sync');
|
|
|
36
36
|
// ============================================================
|
|
37
37
|
|
|
38
38
|
const SKILLS_DIR = PATHS.skills;
|
|
39
|
-
const CORRECTIONS_DIR =
|
|
39
|
+
const CORRECTIONS_DIR = PATHS.corrections;
|
|
40
40
|
|
|
41
41
|
// ============================================================
|
|
42
42
|
// Data Collection
|
|
@@ -386,7 +386,7 @@ async function runPromotionWizard(data) {
|
|
|
386
386
|
output: process.stdout
|
|
387
387
|
});
|
|
388
388
|
|
|
389
|
-
const prompt = (q) =>
|
|
389
|
+
const prompt = (q) => rl.question(q);
|
|
390
390
|
|
|
391
391
|
console.log(color('cyan', 'Pattern Promotion Wizard'));
|
|
392
392
|
console.log('');
|
|
@@ -435,10 +435,15 @@ function appendToDecisions(pattern) {
|
|
|
435
435
|
const entry = `\n## ${date} - Promoted Pattern\n\n**Rule**: ${pattern}\n**Source**: Aggregated from learnings (3+ occurrences)\n\n`;
|
|
436
436
|
|
|
437
437
|
content += entry;
|
|
438
|
-
writeFile(decisionsPath, content);
|
|
439
438
|
|
|
440
|
-
//
|
|
441
|
-
|
|
439
|
+
// Route through orchestrator for locking and dedup
|
|
440
|
+
try {
|
|
441
|
+
const { writeToDecisions } = require('./flow-learning-orchestrator');
|
|
442
|
+
writeToDecisions({ content, entryText: pattern, caller: 'flow-aggregate/appendToDecisions', syncRules: true }).catch(() => {});
|
|
443
|
+
} catch (_err) {
|
|
444
|
+
writeFile(decisionsPath, content);
|
|
445
|
+
syncDecisionsToRules();
|
|
446
|
+
}
|
|
442
447
|
}
|
|
443
448
|
|
|
444
449
|
/**
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
const fs = require('node:fs');
|
|
17
17
|
const path = require('node:path');
|
|
18
|
-
const { getProjectRoot, getConfig, color, success, warn, error, safeJsonParse } = require('./flow-utils');
|
|
18
|
+
const { getProjectRoot, getConfig, color, success, warn, error, safeJsonParse, PATHS } = require('./flow-utils');
|
|
19
19
|
const {
|
|
20
20
|
findSimilarItems,
|
|
21
21
|
generateAIDecisionPrompt,
|
|
@@ -24,10 +24,8 @@ const {
|
|
|
24
24
|
} = require('./flow-semantic-match');
|
|
25
25
|
const { BaseScanner, PROJECT_ROOT } = require('./flow-scanner-base');
|
|
26
26
|
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const INDEX_PATH = path.join(STATE_DIR, 'api-index.json');
|
|
30
|
-
const MAP_PATH = path.join(STATE_DIR, 'api-map.md');
|
|
27
|
+
const INDEX_PATH = path.join(PATHS.state, 'api-index.json');
|
|
28
|
+
const MAP_PATH = path.join(PATHS.state, 'api-map.md');
|
|
31
29
|
|
|
32
30
|
// ============================================================
|
|
33
31
|
// Configuration
|
|
@@ -568,7 +566,7 @@ class APIScanner extends BaseScanner {
|
|
|
568
566
|
*/
|
|
569
567
|
save() {
|
|
570
568
|
this.prune();
|
|
571
|
-
fs.mkdirSync(
|
|
569
|
+
fs.mkdirSync(PATHS.state, { recursive: true });
|
|
572
570
|
fs.writeFileSync(INDEX_PATH, JSON.stringify(this.registry, null, 2));
|
|
573
571
|
success(`Saved to ${path.relative(PROJECT_ROOT, INDEX_PATH)}`);
|
|
574
572
|
}
|
package/scripts/flow-audit.js
CHANGED
|
@@ -95,11 +95,24 @@ function findTodos() {
|
|
|
95
95
|
if (!line) continue;
|
|
96
96
|
const match = line.match(/^(.+):(\d+):(.+)$/);
|
|
97
97
|
if (match) {
|
|
98
|
+
const file = match[1];
|
|
99
|
+
const text = match[3].trim();
|
|
100
|
+
|
|
101
|
+
// Exclude self-references: the TODO scanner's own code mentions
|
|
102
|
+
// TODO/FIXME/HACK as search patterns, not as actual action items.
|
|
103
|
+
// Also exclude lines that are clearly about scanning for TODOs
|
|
104
|
+
// (e.g., pattern arrays, grep invocations, variable names).
|
|
105
|
+
if (file === 'scripts/flow-audit.js') continue;
|
|
106
|
+
|
|
107
|
+
// Skip lines where the pattern appears only inside a string literal
|
|
108
|
+
// used as a search term (e.g., patterns = ['TODO', 'FIXME', ...])
|
|
109
|
+
if (/\[\s*['"](?:TODO|FIXME|HACK|WORKAROUND|TEMPORARY)['"]/.test(text)) continue;
|
|
110
|
+
|
|
98
111
|
results.push({
|
|
99
112
|
type: pattern,
|
|
100
|
-
file
|
|
113
|
+
file,
|
|
101
114
|
line: parseInt(match[2], 10),
|
|
102
|
-
text
|
|
115
|
+
text
|
|
103
116
|
});
|
|
104
117
|
}
|
|
105
118
|
}
|
|
@@ -55,8 +55,6 @@ try {
|
|
|
55
55
|
// Smart context gatherer not available - that's ok
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const PROJECT_ROOT = getProjectRoot();
|
|
59
|
-
|
|
60
58
|
// ============================================================
|
|
61
59
|
// Index Freshness Check
|
|
62
60
|
// ============================================================
|
|
@@ -241,7 +239,7 @@ function inferTaskType(keywords) {
|
|
|
241
239
|
*/
|
|
242
240
|
function searchTraces(keywords) {
|
|
243
241
|
const results = [];
|
|
244
|
-
const tracesDir = path.join(PATHS.traces ||
|
|
242
|
+
const tracesDir = path.join(PATHS.traces || PATHS.traces);
|
|
245
243
|
|
|
246
244
|
if (!fs.existsSync(tracesDir)) return results;
|
|
247
245
|
|
|
@@ -278,7 +276,7 @@ function searchTraces(keywords) {
|
|
|
278
276
|
|
|
279
277
|
results.push({
|
|
280
278
|
source: 'trace',
|
|
281
|
-
path: path.relative(
|
|
279
|
+
path: path.relative(PATHS.root, tracePath),
|
|
282
280
|
name: traceName,
|
|
283
281
|
query: queryMatch ? queryMatch[1] : traceName,
|
|
284
282
|
status: statusMatch ? statusMatch[1] : 'unknown',
|
|
@@ -415,7 +413,7 @@ function searchComponentIndex(keywords, config = null) {
|
|
|
415
413
|
*/
|
|
416
414
|
function grepCodebase(keywords, maxResults = 10, config = null) {
|
|
417
415
|
const results = [];
|
|
418
|
-
const srcDir = path.join(
|
|
416
|
+
const srcDir = path.join(PATHS.root, 'src');
|
|
419
417
|
|
|
420
418
|
if (!fs.existsSync(srcDir)) return results;
|
|
421
419
|
|
|
@@ -453,7 +451,7 @@ function grepCodebase(keywords, maxResults = 10, config = null) {
|
|
|
453
451
|
for (const file of files) {
|
|
454
452
|
if (results.length >= effectiveMaxResults) break;
|
|
455
453
|
|
|
456
|
-
const relPath = path.relative(
|
|
454
|
+
const relPath = path.relative(PATHS.root, file);
|
|
457
455
|
if (!results.some(r => r.path === relPath)) {
|
|
458
456
|
// Optionally read file content with truncation
|
|
459
457
|
let content = null;
|
|
@@ -637,11 +635,9 @@ async function enrichWithLSP(fileResults, config) {
|
|
|
637
635
|
if (filesToEnrich.length === 0) return fileResults;
|
|
638
636
|
|
|
639
637
|
try {
|
|
640
|
-
// Create timeout with cleanup
|
|
641
|
-
|
|
642
|
-
const timeoutPromise =
|
|
643
|
-
timeoutId = setTimeout(() => resolve(filesToEnrich), timeout);
|
|
644
|
-
});
|
|
638
|
+
// Create timeout with cleanup using AbortController
|
|
639
|
+
const ac = new AbortController();
|
|
640
|
+
const timeoutPromise = require('node:timers/promises').setTimeout(timeout, filesToEnrich, { signal: ac.signal }).catch(() => filesToEnrich);
|
|
645
641
|
|
|
646
642
|
const enriched = await Promise.race([
|
|
647
643
|
Promise.all(filesToEnrich.map(async (result) => {
|
|
@@ -670,7 +666,7 @@ async function enrichWithLSP(fileResults, config) {
|
|
|
670
666
|
]);
|
|
671
667
|
|
|
672
668
|
// Clean up timeout to prevent resource leak
|
|
673
|
-
|
|
669
|
+
ac.abort();
|
|
674
670
|
|
|
675
671
|
// Merge enriched results back into full list
|
|
676
672
|
const enrichedMap = new Map(enriched.map(r => [r.path, r]));
|
|
@@ -202,6 +202,7 @@ function loadAutoPatterns() {
|
|
|
202
202
|
|
|
203
203
|
/**
|
|
204
204
|
* Save auto-captured patterns back to feedback-patterns.md
|
|
205
|
+
* Routes through the learning orchestrator for locking and dedup.
|
|
205
206
|
* @param {Array} patterns - Array of pattern objects
|
|
206
207
|
*/
|
|
207
208
|
function saveAutoPatterns(patterns) {
|
|
@@ -211,31 +212,35 @@ function saveAutoPatterns(patterns) {
|
|
|
211
212
|
}
|
|
212
213
|
|
|
213
214
|
try {
|
|
214
|
-
|
|
215
|
+
const { modifyFeedbackPatterns } = require('./flow-learning-orchestrator');
|
|
215
216
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
`| ${p.date} | ${p.pattern} | ${p.source} | ${p.count} | ${p.confidence}% | ${p.status} |`
|
|
219
|
-
);
|
|
217
|
+
modifyFeedbackPatterns((currentContent) => {
|
|
218
|
+
let content = currentContent;
|
|
220
219
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (content.includes('## Auto-Captured Patterns')) {
|
|
225
|
-
content = content.replace(
|
|
226
|
-
/## Auto-Captured Patterns[\s\S]*?(?=\n## |\n---\n\n## |$)/,
|
|
227
|
-
newSection + '\n\n'
|
|
220
|
+
// Build new table (using DRY constants)
|
|
221
|
+
const rows = patterns.map(p =>
|
|
222
|
+
`| ${p.date} | ${p.pattern} | ${p.source} | ${p.count} | ${p.confidence}% | ${p.status} |`
|
|
228
223
|
);
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
224
|
+
|
|
225
|
+
const newSection = `${TABLE_FORMAT.sectionHeader}\n\n${TABLE_FORMAT.header}\n${TABLE_FORMAT.separator}\n${rows.join('\n')}`;
|
|
226
|
+
|
|
227
|
+
// Replace or add section
|
|
228
|
+
if (content.includes('## Auto-Captured Patterns')) {
|
|
229
|
+
content = content.replace(
|
|
230
|
+
/## Auto-Captured Patterns[\s\S]*?(?=\n## |\n---\n\n## |$)/,
|
|
231
|
+
newSection + '\n\n'
|
|
232
|
+
);
|
|
233
233
|
} else {
|
|
234
|
-
|
|
234
|
+
// Add before "## Promotion History" or at the end
|
|
235
|
+
if (content.includes('## Promotion History')) {
|
|
236
|
+
content = content.replace('## Promotion History', newSection + '\n\n---\n\n## Promotion History');
|
|
237
|
+
} else {
|
|
238
|
+
content = content.trimEnd() + '\n\n---\n\n' + newSection + '\n';
|
|
239
|
+
}
|
|
235
240
|
}
|
|
236
|
-
}
|
|
237
241
|
|
|
238
|
-
|
|
242
|
+
return { content, entryText: null }; // Bulk rewrite, no single entry dedup
|
|
243
|
+
}, { caller: 'flow-auto-learn/saveAutoPatterns', skipDedup: true });
|
|
239
244
|
} catch (err) {
|
|
240
245
|
warn(`Could not save feedback-patterns.md: ${err.message}`);
|
|
241
246
|
}
|
|
@@ -449,6 +454,7 @@ function handlePromotion(pattern, config) {
|
|
|
449
454
|
|
|
450
455
|
/**
|
|
451
456
|
* Promote pattern to decisions.md
|
|
457
|
+
* Routes through the learning orchestrator for locking and dedup.
|
|
452
458
|
* @param {Object} pattern - Pattern to promote
|
|
453
459
|
*/
|
|
454
460
|
function promoteToDecisions(pattern) {
|
|
@@ -458,37 +464,39 @@ function promoteToDecisions(pattern) {
|
|
|
458
464
|
}
|
|
459
465
|
|
|
460
466
|
try {
|
|
461
|
-
|
|
467
|
+
const { modifyDecisions } = require('./flow-learning-orchestrator');
|
|
468
|
+
const today = getTodayDate();
|
|
469
|
+
const escapedPattern = pattern.pattern.replace(/[#*_\[\]()\\]/g, '\\$&');
|
|
462
470
|
|
|
463
|
-
|
|
464
|
-
|
|
471
|
+
modifyDecisions((currentContent) => {
|
|
472
|
+
let content = currentContent;
|
|
465
473
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
474
|
+
// Find Coding Standards section
|
|
475
|
+
const sectionHeader = '## Coding Standards';
|
|
470
476
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
477
|
+
if (!content.includes(sectionHeader)) {
|
|
478
|
+
warn(`Could not promote pattern: Coding Standards section not found in decisions.md`);
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Generate rule entry
|
|
483
|
+
const ruleEntry = `\n### ${escapedPattern} (${today})
|
|
475
484
|
**Source**: Auto-learned from ${pattern.count} occurrences (${pattern.source})
|
|
476
485
|
**Rule**: [Describe the pattern rule here]
|
|
477
486
|
`;
|
|
478
487
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
488
|
+
// Insert after Coding Standards header
|
|
489
|
+
const insertPoint = content.indexOf(sectionHeader) + sectionHeader.length;
|
|
490
|
+
const nextSection = content.indexOf('\n## ', insertPoint);
|
|
482
491
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
content = content.slice(0, insertPoint) + ruleEntry + content.slice(insertPoint);
|
|
489
|
-
}
|
|
492
|
+
if (nextSection > insertPoint) {
|
|
493
|
+
content = content.slice(0, nextSection) + ruleEntry + content.slice(nextSection);
|
|
494
|
+
} else {
|
|
495
|
+
content = content.slice(0, insertPoint) + ruleEntry + content.slice(insertPoint);
|
|
496
|
+
}
|
|
490
497
|
|
|
491
|
-
|
|
498
|
+
return { content, entryText: pattern.pattern };
|
|
499
|
+
}, { caller: 'flow-auto-learn/promoteToDecisions', syncRules: true });
|
|
492
500
|
|
|
493
501
|
// Update pattern status
|
|
494
502
|
const patterns = loadAutoPatterns();
|
|
@@ -497,14 +505,6 @@ function promoteToDecisions(pattern) {
|
|
|
497
505
|
updated.status = 'Promoted';
|
|
498
506
|
saveAutoPatterns(patterns);
|
|
499
507
|
}
|
|
500
|
-
|
|
501
|
-
// Sync rules
|
|
502
|
-
try {
|
|
503
|
-
require('./flow-rules-sync');
|
|
504
|
-
} catch (syncErr) {
|
|
505
|
-
// Log the failure for debugging
|
|
506
|
-
info(`Note: Rules sync skipped - ${syncErr.code === 'MODULE_NOT_FOUND' ? 'module not found' : syncErr.message}`);
|
|
507
|
-
}
|
|
508
508
|
} catch (err) {
|
|
509
509
|
warn(`Could not promote to decisions.md: ${err.message}`);
|
|
510
510
|
}
|