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
|
@@ -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
|
|
@@ -430,9 +430,7 @@ async function orchestrateBulk(taskIds, options = {}) {
|
|
|
430
430
|
* @param {number} ms - Milliseconds to sleep
|
|
431
431
|
* @returns {Promise}
|
|
432
432
|
*/
|
|
433
|
-
|
|
434
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
435
|
-
}
|
|
433
|
+
const { setTimeout: sleep } = require('node:timers/promises');
|
|
436
434
|
|
|
437
435
|
/**
|
|
438
436
|
* Get ready tasks from ready.json
|
package/scripts/flow-cascade.js
CHANGED
|
@@ -28,9 +28,10 @@ const {
|
|
|
28
28
|
getConfig,
|
|
29
29
|
fileExists,
|
|
30
30
|
writeJson,
|
|
31
|
+
safeJsonParse,
|
|
31
32
|
printHeader,
|
|
32
33
|
printSection,
|
|
33
|
-
showHelp: showHelpGeneric
|
|
34
|
+
showHelp: showHelpGeneric, PATHS
|
|
34
35
|
} = require('./flow-utils');
|
|
35
36
|
|
|
36
37
|
// ============================================================
|
|
@@ -92,7 +93,7 @@ const CATEGORY_PATTERNS = [
|
|
|
92
93
|
// State Management
|
|
93
94
|
// ============================================================
|
|
94
95
|
|
|
95
|
-
const STATE_PATH = path.join(
|
|
96
|
+
const STATE_PATH = path.join(PATHS.state, 'cascade-state.json');
|
|
96
97
|
|
|
97
98
|
/**
|
|
98
99
|
* In-memory failure tracker.
|
|
@@ -113,8 +114,7 @@ function loadState() {
|
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
try {
|
|
116
|
-
const
|
|
117
|
-
const state = JSON.parse(content);
|
|
117
|
+
const state = safeJsonParse(STATE_PATH, {});
|
|
118
118
|
|
|
119
119
|
// Clean up expired entries
|
|
120
120
|
const now = Date.now();
|
|
@@ -23,12 +23,10 @@
|
|
|
23
23
|
const fs = require('node:fs');
|
|
24
24
|
const path = require('node:path');
|
|
25
25
|
const { execSync, spawnSync } = require('node:child_process');
|
|
26
|
-
const { getProjectRoot, getConfig, colors: c } = require('./flow-utils');
|
|
26
|
+
const { getProjectRoot, getConfig, colors: c, PATHS } = require('./flow-utils');
|
|
27
27
|
const { success: printSuccess, warn: printWarn } = require('./flow-output');
|
|
28
28
|
|
|
29
|
-
const
|
|
30
|
-
const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
|
|
31
|
-
const CHECKPOINTS_DIR = path.join(WORKFLOW_DIR, 'checkpoints');
|
|
29
|
+
const CHECKPOINTS_DIR = PATHS.checkpoints;
|
|
32
30
|
const CHECKPOINT_LOG = path.join(CHECKPOINTS_DIR, 'checkpoint-log.json');
|
|
33
31
|
|
|
34
32
|
// Alias getConfig as loadConfig for minimal code changes
|
|
@@ -182,7 +180,7 @@ class Checkpoint {
|
|
|
182
180
|
const snapshots = {};
|
|
183
181
|
|
|
184
182
|
for (const relPath of stateFiles) {
|
|
185
|
-
const srcPath = path.join(
|
|
183
|
+
const srcPath = path.join(PATHS.workflow, relPath);
|
|
186
184
|
try {
|
|
187
185
|
const content = fs.readFileSync(srcPath, 'utf-8');
|
|
188
186
|
const destPath = path.join(snapshotDir, relPath.replace(/\//g, '_'));
|
|
@@ -205,7 +203,7 @@ class Checkpoint {
|
|
|
205
203
|
hasGitChanges() {
|
|
206
204
|
try {
|
|
207
205
|
const result = spawnSync('git', ['status', '--porcelain'], {
|
|
208
|
-
cwd:
|
|
206
|
+
cwd: PATHS.root,
|
|
209
207
|
encoding: 'utf-8'
|
|
210
208
|
});
|
|
211
209
|
return result.stdout && result.stdout.trim().length > 0;
|
|
@@ -220,19 +218,19 @@ class Checkpoint {
|
|
|
220
218
|
createGitCommit(message) {
|
|
221
219
|
try {
|
|
222
220
|
// Stage all changes
|
|
223
|
-
spawnSync('git', ['add', '-A'], { cwd:
|
|
221
|
+
spawnSync('git', ['add', '-A'], { cwd: PATHS.root });
|
|
224
222
|
|
|
225
223
|
// Create commit
|
|
226
224
|
const commitMessage = `${this.config.commitPrefix} ${message}`;
|
|
227
225
|
const result = spawnSync('git', ['commit', '-m', commitMessage], {
|
|
228
|
-
cwd:
|
|
226
|
+
cwd: PATHS.root,
|
|
229
227
|
encoding: 'utf-8'
|
|
230
228
|
});
|
|
231
229
|
|
|
232
230
|
if (result.status === 0) {
|
|
233
231
|
// Get commit hash
|
|
234
232
|
const hashResult = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {
|
|
235
|
-
cwd:
|
|
233
|
+
cwd: PATHS.root,
|
|
236
234
|
encoding: 'utf-8'
|
|
237
235
|
});
|
|
238
236
|
return hashResult.stdout.trim();
|
|
@@ -264,7 +262,7 @@ class Checkpoint {
|
|
|
264
262
|
for (const [relPath, snapshotPath] of Object.entries(checkpoint.stateSnapshot)) {
|
|
265
263
|
try {
|
|
266
264
|
const content = fs.readFileSync(snapshotPath, 'utf-8');
|
|
267
|
-
const destPath = path.join(
|
|
265
|
+
const destPath = path.join(PATHS.workflow, relPath);
|
|
268
266
|
fs.writeFileSync(destPath, content);
|
|
269
267
|
} catch (err) {
|
|
270
268
|
if (err.code !== 'ENOENT') {
|
|
@@ -284,14 +282,14 @@ class Checkpoint {
|
|
|
284
282
|
// Find commits since checkpoint
|
|
285
283
|
const logResult = spawnSync('git', [
|
|
286
284
|
'log', '--oneline', `${checkpoint.gitCommit}..HEAD`
|
|
287
|
-
], { cwd:
|
|
285
|
+
], { cwd: PATHS.root, encoding: 'utf-8' });
|
|
288
286
|
|
|
289
287
|
const commitsSince = logResult.stdout.trim().split('\n').filter(l => l).length;
|
|
290
288
|
|
|
291
289
|
if (commitsSince > 0) {
|
|
292
290
|
// Soft reset to checkpoint
|
|
293
291
|
spawnSync('git', ['reset', '--soft', checkpoint.gitCommit], {
|
|
294
|
-
cwd:
|
|
292
|
+
cwd: PATHS.root
|
|
295
293
|
});
|
|
296
294
|
results.gitRestored = true;
|
|
297
295
|
}
|
|
@@ -354,7 +352,6 @@ class Checkpoint {
|
|
|
354
352
|
}
|
|
355
353
|
}
|
|
356
354
|
|
|
357
|
-
|
|
358
355
|
/**
|
|
359
356
|
* Format checkpoint list for display
|
|
360
357
|
*/
|
|
@@ -22,15 +22,13 @@ const { getProjectRoot, getConfig, PATHS, colors, readJson } = require('./flow-u
|
|
|
22
22
|
const { success, error: errorMsg } = require('./flow-output');
|
|
23
23
|
const { safeGrep, safeFind, escapeRegex } = require('./flow-security');
|
|
24
24
|
|
|
25
|
-
const PROJECT_ROOT = getProjectRoot();
|
|
26
|
-
|
|
27
25
|
/**
|
|
28
26
|
* Resolve a path alias (e.g., '@/*') from tsconfig.json compilerOptions.paths.
|
|
29
27
|
* Returns the base directory (e.g., 'src') or null if not found.
|
|
30
28
|
*/
|
|
31
29
|
function resolvePathAlias(alias) {
|
|
32
30
|
try {
|
|
33
|
-
const tsconfigPath = path.join(
|
|
31
|
+
const tsconfigPath = path.join(PATHS.root, 'tsconfig.json');
|
|
34
32
|
if (!fs.existsSync(tsconfigPath)) return null;
|
|
35
33
|
const content = fs.readFileSync(tsconfigPath, 'utf-8');
|
|
36
34
|
// Strip comments then use safeJsonParseString for prototype-pollution protection
|
|
@@ -51,7 +49,7 @@ function resolvePathAlias(alias) {
|
|
|
51
49
|
*/
|
|
52
50
|
function detectSourceRoot() {
|
|
53
51
|
for (const dir of ['src', 'app', 'lib']) {
|
|
54
|
-
if (fs.existsSync(path.join(
|
|
52
|
+
if (fs.existsSync(path.join(PATHS.root, dir))) return dir;
|
|
55
53
|
}
|
|
56
54
|
return '.'; // fallback to project root
|
|
57
55
|
}
|
|
@@ -69,14 +67,14 @@ function detectSourceRoot() {
|
|
|
69
67
|
function analyzeRelationships(filePath) {
|
|
70
68
|
const fullPath = path.isAbsolute(filePath)
|
|
71
69
|
? filePath
|
|
72
|
-
: path.join(
|
|
70
|
+
: path.join(PATHS.root, filePath);
|
|
73
71
|
|
|
74
72
|
if (!fs.existsSync(fullPath)) {
|
|
75
73
|
return { error: 'File not found' };
|
|
76
74
|
}
|
|
77
75
|
|
|
78
76
|
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
79
|
-
const relPath = path.relative(
|
|
77
|
+
const relPath = path.relative(PATHS.root, fullPath);
|
|
80
78
|
|
|
81
79
|
const relationships = {
|
|
82
80
|
file: relPath,
|
|
@@ -371,10 +369,10 @@ function resolveImportPath(importSource, fromDir) {
|
|
|
371
369
|
for (const ext of extensions) {
|
|
372
370
|
const candidate = path.resolve(fromDir, importSource + ext);
|
|
373
371
|
// Path containment check — reject paths that escape project root
|
|
374
|
-
if (!candidate.startsWith(
|
|
375
|
-
const relPath = path.relative(
|
|
372
|
+
if (!candidate.startsWith(PATHS.root + path.sep) && candidate !== PATHS.root) continue;
|
|
373
|
+
const relPath = path.relative(PATHS.root, candidate);
|
|
376
374
|
|
|
377
|
-
if (fs.existsSync(path.join(
|
|
375
|
+
if (fs.existsSync(path.join(PATHS.root, relPath))) {
|
|
378
376
|
return relPath;
|
|
379
377
|
}
|
|
380
378
|
}
|
|
@@ -430,7 +428,7 @@ async function findFilesImporting(filePath) {
|
|
|
430
428
|
// Use safe grep with escaped pattern to prevent injection
|
|
431
429
|
const pattern = `from.*${escapeRegex(basename)}`;
|
|
432
430
|
return safeGrep(pattern, {
|
|
433
|
-
cwd:
|
|
431
|
+
cwd: PATHS.root,
|
|
434
432
|
searchDir: 'src/',
|
|
435
433
|
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
|
436
434
|
maxResults: 20
|
|
@@ -443,7 +441,7 @@ async function findFilesImporting(filePath) {
|
|
|
443
441
|
async function searchCodebase(keyword, maxResults = 10) {
|
|
444
442
|
// Use safe grep with escaped pattern to prevent injection
|
|
445
443
|
return safeGrep(keyword, {
|
|
446
|
-
cwd:
|
|
444
|
+
cwd: PATHS.root,
|
|
447
445
|
searchDir: 'src/',
|
|
448
446
|
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
|
449
447
|
maxResults
|
|
@@ -678,7 +676,7 @@ async function main() {
|
|
|
678
676
|
|
|
679
677
|
// Use safe find to prevent command injection
|
|
680
678
|
const files = safeFind(dir, {
|
|
681
|
-
cwd:
|
|
679
|
+
cwd: PATHS.root,
|
|
682
680
|
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
|
683
681
|
maxResults: 100
|
|
684
682
|
});
|
|
@@ -31,9 +31,9 @@ const { loadStats } = require('./flow-stats-collector');
|
|
|
31
31
|
// Constants
|
|
32
32
|
// ============================================================
|
|
33
33
|
|
|
34
|
-
const COMMUNITY_SCORES_PATH =
|
|
35
|
-
const COMMUNITY_ROUTING_PATH =
|
|
36
|
-
const SYNC_QUEUE_PATH = path.join(PATHS.
|
|
34
|
+
const COMMUNITY_SCORES_PATH = PATHS.communityScores;
|
|
35
|
+
const COMMUNITY_ROUTING_PATH = PATHS.communityRouting;
|
|
36
|
+
const SYNC_QUEUE_PATH = path.join(PATHS.state, 'sync-queue.json');
|
|
37
37
|
|
|
38
38
|
const DEFAULT_SYNC_CONFIG = {
|
|
39
39
|
enabled: false,
|
|
@@ -78,7 +78,7 @@ function isSyncEnabled() {
|
|
|
78
78
|
if (!config.enabled) return false;
|
|
79
79
|
|
|
80
80
|
// Check for auth token (set by `wogi login`)
|
|
81
|
-
const authPath = path.join(PATHS.
|
|
81
|
+
const authPath = path.join(PATHS.state, 'auth.json');
|
|
82
82
|
try {
|
|
83
83
|
if (!fs.existsSync(authPath)) return false;
|
|
84
84
|
const auth = readJson(authPath, {});
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Wogi Flow - Community Knowledge Module
|
|
5
3
|
*
|
|
@@ -1092,25 +1090,19 @@ function mergePatterns(items) {
|
|
|
1092
1090
|
}
|
|
1093
1091
|
|
|
1094
1092
|
if (newRows.length > 0) {
|
|
1095
|
-
//
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
if (process.env.DEBUG) {
|
|
1103
|
-
console.error(`[flow-community] Failed to write feedback-patterns.md: ${err.message}`);
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
} else {
|
|
1107
|
-
// Append at end
|
|
1108
|
-
try {
|
|
1109
|
-
fs.writeFileSync(filePath, content.trimEnd() + '\n' + newRows.join('\n') + '\n', 'utf-8');
|
|
1110
|
-
} catch (err) {
|
|
1111
|
-
if (process.env.DEBUG) {
|
|
1112
|
-
console.error(`[flow-community] Failed to write feedback-patterns.md: ${err.message}`);
|
|
1093
|
+
// Route through orchestrator for locking and dedup
|
|
1094
|
+
try {
|
|
1095
|
+
const { modifyFeedbackPatterns } = require('./flow-learning-orchestrator');
|
|
1096
|
+
modifyFeedbackPatterns((curContent) => {
|
|
1097
|
+
const tableEnd = curContent.indexOf('\n\n### ');
|
|
1098
|
+
if (tableEnd !== -1) {
|
|
1099
|
+
return { content: curContent.slice(0, tableEnd) + '\n' + newRows.join('\n') + curContent.slice(tableEnd) };
|
|
1113
1100
|
}
|
|
1101
|
+
return { content: curContent.trimEnd() + '\n' + newRows.join('\n') + '\n' };
|
|
1102
|
+
}, { caller: 'flow-community/mergePatterns', skipDedup: true });
|
|
1103
|
+
} catch (err) {
|
|
1104
|
+
if (process.env.DEBUG) {
|
|
1105
|
+
console.error(`[flow-community] Failed to write feedback-patterns.md: ${err.message}`);
|
|
1114
1106
|
}
|
|
1115
1107
|
}
|
|
1116
1108
|
}
|
|
@@ -27,8 +27,6 @@
|
|
|
27
27
|
* bulkLoop, corrections, agents (top-level list)
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
'use strict';
|
|
31
|
-
|
|
32
30
|
// ============================================================
|
|
33
31
|
// Static Data Constants (large objects extracted for readability)
|
|
34
32
|
// ============================================================
|
|
@@ -25,7 +25,8 @@ const {
|
|
|
25
25
|
warn,
|
|
26
26
|
error,
|
|
27
27
|
parseFlags,
|
|
28
|
-
outputJson
|
|
28
|
+
outputJson,
|
|
29
|
+
safeJsonParse
|
|
29
30
|
} = require('./flow-utils');
|
|
30
31
|
|
|
31
32
|
// ============================================================
|
|
@@ -220,11 +221,14 @@ function exportConfig() {
|
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
function resetConfig() {
|
|
223
|
-
const configPath =
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
const configPath = PATHS.config;
|
|
225
|
+
const config = safeJsonParse(configPath, null);
|
|
226
|
+
if (!config) {
|
|
227
|
+
error('Failed to read config.json');
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
227
230
|
|
|
231
|
+
try {
|
|
228
232
|
// Keep only structural keys, remove user overrides
|
|
229
233
|
const minimal = {
|
|
230
234
|
$schema: config.$schema,
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Wogi Flow - Config Loading and Management
|
|
5
3
|
*
|
|
@@ -13,7 +11,7 @@
|
|
|
13
11
|
const fs = require('node:fs');
|
|
14
12
|
const path = require('node:path');
|
|
15
13
|
const { PATHS, PROJECT_ROOT, isPathWithinProject } = require('./flow-paths');
|
|
16
|
-
const { checkForDangerousKeys, readJson, writeJson, acquireLock } = require('./flow-io');
|
|
14
|
+
const { checkForDangerousKeys, readJson, writeJson, acquireLock, safeJsonParse, safeJsonParseString } = require('./flow-io');
|
|
17
15
|
const { warn } = require('./flow-output');
|
|
18
16
|
|
|
19
17
|
// Late-loaded to avoid circular dependency
|
|
@@ -296,23 +294,12 @@ function getConfig() {
|
|
|
296
294
|
}
|
|
297
295
|
|
|
298
296
|
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
} catch (err) {
|
|
303
|
-
console.warn(`Warning: Invalid JSON in config.json: ${err.message}`);
|
|
297
|
+
const rawConfig = safeJsonParseString(configContent, null);
|
|
298
|
+
if (!rawConfig || typeof rawConfig !== 'object') {
|
|
299
|
+
console.warn('Warning: Invalid or dangerous JSON in config.json');
|
|
304
300
|
return {};
|
|
305
301
|
}
|
|
306
302
|
|
|
307
|
-
// Prototype pollution check on config
|
|
308
|
-
if (rawConfig && typeof rawConfig === 'object') {
|
|
309
|
-
const dangerousKeyError = checkForDangerousKeys(rawConfig);
|
|
310
|
-
if (dangerousKeyError) {
|
|
311
|
-
console.warn(`Warning: Dangerous keys in config.json: ${dangerousKeyError}`);
|
|
312
|
-
return {};
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
303
|
// Validate on first load (DEBUG mode or explicit request)
|
|
317
304
|
if (process.env.DEBUG || process.env.VALIDATE_CONFIG) {
|
|
318
305
|
validateConfig(rawConfig);
|
|
@@ -366,27 +353,7 @@ function getConfig() {
|
|
|
366
353
|
* Use this when you need to read/modify config without resolving variables
|
|
367
354
|
*/
|
|
368
355
|
function getRawConfig() {
|
|
369
|
-
|
|
370
|
-
if (!fs.existsSync(configPath)) return {};
|
|
371
|
-
|
|
372
|
-
try {
|
|
373
|
-
const content = fs.readFileSync(configPath, 'utf-8');
|
|
374
|
-
const parsed = JSON.parse(content);
|
|
375
|
-
|
|
376
|
-
// Prototype pollution check
|
|
377
|
-
if (parsed && typeof parsed === 'object') {
|
|
378
|
-
const dangerousKeyError = checkForDangerousKeys(parsed);
|
|
379
|
-
if (dangerousKeyError) {
|
|
380
|
-
console.warn(`Warning: Dangerous keys in config.json: ${dangerousKeyError}`);
|
|
381
|
-
return {};
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
return parsed;
|
|
386
|
-
} catch (err) {
|
|
387
|
-
console.warn(`Warning: Could not parse config.json: ${err.message}`);
|
|
388
|
-
return {};
|
|
389
|
-
}
|
|
356
|
+
return safeJsonParse(PATHS.config, {});
|
|
390
357
|
}
|
|
391
358
|
|
|
392
359
|
/**
|
|
@@ -439,6 +406,48 @@ function getConfigValue(configPath, defaultValue = null) {
|
|
|
439
406
|
return value;
|
|
440
407
|
}
|
|
441
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Apply a value at a dot-notation path on a config object.
|
|
411
|
+
* Shared logic between setConfigValue (async, locked) and setConfigValueSync.
|
|
412
|
+
*
|
|
413
|
+
* @param {string} configPath - Dot-notation path (e.g., 'parallel.enabled')
|
|
414
|
+
* @param {*} newValue - New value to set
|
|
415
|
+
* @param {object} config - Config object to mutate
|
|
416
|
+
*/
|
|
417
|
+
function _applyConfigPath(configPath, newValue, config) {
|
|
418
|
+
const parts = configPath.split('.');
|
|
419
|
+
let obj = config;
|
|
420
|
+
|
|
421
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
422
|
+
const part = parts[i];
|
|
423
|
+
if (DANGEROUS_CONFIG_PROPS.has(part)) {
|
|
424
|
+
throw new Error(`Dangerous config key: ${part}`);
|
|
425
|
+
}
|
|
426
|
+
if (!Object.hasOwn(obj, part)) {
|
|
427
|
+
obj[part] = {};
|
|
428
|
+
}
|
|
429
|
+
obj = obj[part];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const lastPart = parts[parts.length - 1];
|
|
433
|
+
if (DANGEROUS_CONFIG_PROPS.has(lastPart)) {
|
|
434
|
+
throw new Error(`Dangerous config key: ${lastPart}`);
|
|
435
|
+
}
|
|
436
|
+
obj[lastPart] = newValue;
|
|
437
|
+
writeJson(PATHS.config, config);
|
|
438
|
+
invalidateConfigCache();
|
|
439
|
+
|
|
440
|
+
// Auto-sync .gitignore only when config keys affect gitignore mappings
|
|
441
|
+
if (configPath.startsWith('testing.') || configPath.startsWith('webmcp.')) {
|
|
442
|
+
try {
|
|
443
|
+
const { syncGitignore } = require('./flow-gitignore');
|
|
444
|
+
syncGitignore(config);
|
|
445
|
+
} catch (_err) {
|
|
446
|
+
// Non-blocking — gitignore sync should never fail config writes
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
442
451
|
/**
|
|
443
452
|
* Update config value (uses locking to prevent race conditions)
|
|
444
453
|
* SECURITY: Always acquires lock before writing to prevent data corruption
|
|
@@ -448,51 +457,23 @@ function getConfigValue(configPath, defaultValue = null) {
|
|
|
448
457
|
* @throws {Error} If lock cannot be acquired after retries
|
|
449
458
|
*/
|
|
450
459
|
async function setConfigValue(configPath, newValue) {
|
|
451
|
-
// Validate path to prevent prototype pollution
|
|
452
460
|
if (!isValidConfigPath(configPath)) {
|
|
453
461
|
throw new Error(`Invalid config path: ${configPath}`);
|
|
454
462
|
}
|
|
455
463
|
|
|
456
|
-
// Use file lock to prevent concurrent writes
|
|
457
464
|
const lockPath = PATHS.config;
|
|
458
465
|
let release;
|
|
459
466
|
|
|
460
467
|
try {
|
|
461
|
-
// More retries with exponential backoff for better reliability
|
|
462
468
|
release = await acquireLock(lockPath, { retries: 5, retryDelay: 100, exponentialBackoff: true });
|
|
463
469
|
} catch (err) {
|
|
464
|
-
// SECURITY: Don't fall back to non-locked write - throw instead
|
|
465
470
|
throw new Error(`Could not acquire config lock after retries: ${err.message}. Config not updated.`, { cause: err });
|
|
466
471
|
}
|
|
467
472
|
|
|
468
473
|
try {
|
|
469
|
-
// Re-read config after acquiring lock (may have changed)
|
|
470
474
|
invalidateConfigCache();
|
|
471
475
|
const config = getConfig();
|
|
472
|
-
|
|
473
|
-
let obj = config;
|
|
474
|
-
|
|
475
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
476
|
-
const part = parts[i];
|
|
477
|
-
if (!Object.hasOwn(obj, part)) {
|
|
478
|
-
obj[part] = {};
|
|
479
|
-
}
|
|
480
|
-
obj = obj[part];
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
obj[parts[parts.length - 1]] = newValue;
|
|
484
|
-
writeJson(PATHS.config, config);
|
|
485
|
-
invalidateConfigCache();
|
|
486
|
-
|
|
487
|
-
// Auto-sync .gitignore only when config keys affect gitignore mappings
|
|
488
|
-
if (configPath.startsWith('testing.') || configPath.startsWith('webmcp.')) {
|
|
489
|
-
try {
|
|
490
|
-
const { syncGitignore } = require('./flow-gitignore');
|
|
491
|
-
syncGitignore(config);
|
|
492
|
-
} catch (err) {
|
|
493
|
-
// Non-blocking — gitignore sync should never fail config writes
|
|
494
|
-
}
|
|
495
|
-
}
|
|
476
|
+
_applyConfigPath(configPath, newValue, config);
|
|
496
477
|
} finally {
|
|
497
478
|
if (release) release();
|
|
498
479
|
}
|
|
@@ -503,36 +484,12 @@ async function setConfigValue(configPath, newValue) {
|
|
|
503
484
|
* Use setConfigValue for concurrent-safe writes
|
|
504
485
|
*/
|
|
505
486
|
function setConfigValueSync(configPath, newValue) {
|
|
506
|
-
// Validate path to prevent prototype pollution
|
|
507
487
|
if (!isValidConfigPath(configPath)) {
|
|
508
488
|
throw new Error(`Invalid config path: ${configPath}`);
|
|
509
489
|
}
|
|
510
490
|
|
|
511
491
|
const config = getConfig();
|
|
512
|
-
|
|
513
|
-
let obj = config;
|
|
514
|
-
|
|
515
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
516
|
-
const part = parts[i];
|
|
517
|
-
if (!Object.hasOwn(obj, part)) {
|
|
518
|
-
obj[part] = {};
|
|
519
|
-
}
|
|
520
|
-
obj = obj[part];
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
obj[parts[parts.length - 1]] = newValue;
|
|
524
|
-
writeJson(PATHS.config, config);
|
|
525
|
-
invalidateConfigCache();
|
|
526
|
-
|
|
527
|
-
// Auto-sync .gitignore only when config keys affect gitignore mappings
|
|
528
|
-
if (configPath.startsWith('testing.') || configPath.startsWith('webmcp.')) {
|
|
529
|
-
try {
|
|
530
|
-
const { syncGitignore } = require('./flow-gitignore');
|
|
531
|
-
syncGitignore(config);
|
|
532
|
-
} catch (err) {
|
|
533
|
-
// Non-blocking — gitignore sync should never fail config writes
|
|
534
|
-
}
|
|
535
|
-
}
|
|
492
|
+
_applyConfigPath(configPath, newValue, config);
|
|
536
493
|
}
|
|
537
494
|
|
|
538
495
|
/**
|