wogiflow 2.4.2 → 2.4.4
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/commands/wogi-start.md +124 -0
- package/.claude/docs/claude-code-compatibility.md +51 -0
- package/.claude/docs/explore-agents.md +11 -0
- package/.claude/settings.json +12 -1
- package/.workflow/models/registry.json +1 -1
- package/bin/flow +11 -1
- package/lib/workspace-contracts.js +599 -0
- package/lib/workspace-intelligence.js +600 -0
- package/lib/workspace-messages.js +441 -0
- package/lib/workspace-routing.js +485 -0
- package/lib/workspace-sync.js +339 -0
- package/lib/workspace.js +1073 -0
- 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 +28 -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-calibration.js +257 -0
- package/scripts/flow-eval-judge.js +10 -1
- package/scripts/flow-eval.js +14 -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 +31 -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/task-created.js +83 -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/task-created.js +15 -0
- 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/postinstall.js +2 -0
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wogiflow",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.4",
|
|
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
|
}
|
|
@@ -23,15 +23,14 @@ const {
|
|
|
23
23
|
fileExists,
|
|
24
24
|
color,
|
|
25
25
|
printHeader,
|
|
26
|
-
printSection, success } = require('./flow-utils');
|
|
26
|
+
printSection, success, PATHS } = require('./flow-utils');
|
|
27
27
|
|
|
28
28
|
// ============================================================
|
|
29
29
|
// Configuration
|
|
30
30
|
// ============================================================
|
|
31
31
|
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const LOGS_DIR = path.join(PROJECT_ROOT, '.workflow', 'logs');
|
|
32
|
+
const BACKGROUND_STATE_PATH = path.join(PATHS.state, 'background-tasks.json');
|
|
33
|
+
const LOGS_DIR = PATHS.logs;
|
|
35
34
|
|
|
36
35
|
// Available background tasks with their configurations
|
|
37
36
|
const AVAILABLE_TASKS = {
|
|
@@ -141,7 +140,7 @@ function runBackgroundTask(taskName, options = {}) {
|
|
|
141
140
|
throw new Error(`Unknown task: ${taskName}. Use 'flow background list' to see available tasks.`);
|
|
142
141
|
}
|
|
143
142
|
|
|
144
|
-
const scriptPath = path.join(
|
|
143
|
+
const scriptPath = path.join(PATHS.root, 'scripts', taskConfig.script);
|
|
145
144
|
|
|
146
145
|
if (!fileExists(scriptPath)) {
|
|
147
146
|
throw new Error(`Script not found: ${taskConfig.script}`);
|
|
@@ -163,7 +162,7 @@ function runBackgroundTask(taskName, options = {}) {
|
|
|
163
162
|
|
|
164
163
|
// Spawn the process
|
|
165
164
|
const child = spawn('node', [scriptPath, ...args], {
|
|
166
|
-
cwd:
|
|
165
|
+
cwd: PATHS.root,
|
|
167
166
|
detached: true,
|
|
168
167
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
169
168
|
env: { ...process.env, BACKGROUND_TASK: 'true', TASK_ID: taskId }
|
|
@@ -18,14 +18,12 @@ const path = require('node:path');
|
|
|
18
18
|
const crypto = require('node:crypto');
|
|
19
19
|
|
|
20
20
|
// Import canonical safeJsonParse from flow-utils (consolidated per code review)
|
|
21
|
-
const { safeJsonParse } = require('./flow-utils');
|
|
21
|
+
const { safeJsonParse, PATHS } = require('./flow-utils');
|
|
22
22
|
|
|
23
23
|
// Project paths
|
|
24
24
|
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const CONFIG_PATH = path.join(WORKFLOW_DIR, 'config.json');
|
|
28
|
-
const SYNC_STATE_PATH = path.join(STATE_DIR, 'bridge-sync.json');
|
|
25
|
+
const CONFIG_PATH = path.join(PATHS.workflow, 'config.json');
|
|
26
|
+
const SYNC_STATE_PATH = path.join(PATHS.state, 'bridge-sync.json');
|
|
29
27
|
|
|
30
28
|
// CLI type to output file mapping (Claude Code only)
|
|
31
29
|
const CLI_OUTPUT_FILES = {
|
|
@@ -60,7 +58,7 @@ function getTemplateChecksum(cliType) {
|
|
|
60
58
|
const templateName = CLI_TEMPLATES[cliType];
|
|
61
59
|
if (!templateName) return '';
|
|
62
60
|
|
|
63
|
-
const templatePath = path.join(
|
|
61
|
+
const templatePath = path.join(PATHS.workflow, 'templates', templateName);
|
|
64
62
|
const content = fs.readFileSync(templatePath, 'utf-8');
|
|
65
63
|
return crypto.createHash('md5').update(content).digest('hex');
|
|
66
64
|
} catch (_err) {
|
|
@@ -94,8 +92,8 @@ function readSyncState() {
|
|
|
94
92
|
function writeSyncState(state) {
|
|
95
93
|
try {
|
|
96
94
|
// Ensure state directory exists
|
|
97
|
-
if (!fs.existsSync(
|
|
98
|
-
fs.mkdirSync(
|
|
95
|
+
if (!fs.existsSync(PATHS.state)) {
|
|
96
|
+
fs.mkdirSync(PATHS.state, { recursive: true });
|
|
99
97
|
}
|
|
100
98
|
fs.writeFileSync(SYNC_STATE_PATH, JSON.stringify(state, null, 2));
|
|
101
99
|
} catch (err) {
|
|
@@ -220,7 +218,7 @@ async function autoSyncBridge(cliType = 'claude-code', options = {}) {
|
|
|
220
218
|
// Load bridges module
|
|
221
219
|
let bridges;
|
|
222
220
|
try {
|
|
223
|
-
bridges = require(
|
|
221
|
+
bridges = require(PATHS.bridges);
|
|
224
222
|
} catch (err) {
|
|
225
223
|
if (process.env.DEBUG) {
|
|
226
224
|
console.error(`[bridge-state] Failed to load bridges: ${err.message}`);
|
|
@@ -294,7 +292,7 @@ function getSyncStatus() {
|
|
|
294
292
|
const check = needsSync(cliType);
|
|
295
293
|
const outputPath = getOutputFilePath(cliType);
|
|
296
294
|
const templateName = CLI_TEMPLATES[cliType];
|
|
297
|
-
const templatePath = templateName ? path.join(
|
|
295
|
+
const templatePath = templateName ? path.join(PATHS.workflow, 'templates', templateName) : null;
|
|
298
296
|
|
|
299
297
|
return {
|
|
300
298
|
'claude-code': {
|
|
@@ -339,9 +339,7 @@ async function runCompact() {
|
|
|
339
339
|
* Sleep for specified milliseconds
|
|
340
340
|
* @param {number} ms - Milliseconds to sleep
|
|
341
341
|
*/
|
|
342
|
-
|
|
343
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
344
|
-
}
|
|
342
|
+
const { setTimeout: sleep } = require('node:timers/promises');
|
|
345
343
|
|
|
346
344
|
/**
|
|
347
345
|
* Format duration in human-readable form
|