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
|
@@ -20,8 +20,6 @@ const { success, warn, error: errorMsg, info } = require('./flow-output');
|
|
|
20
20
|
const { matchSkills, loadSkillContext } = require('./flow-skill-matcher');
|
|
21
21
|
const { getCommand } = require('./flow-script-resolver');
|
|
22
22
|
|
|
23
|
-
const PROJECT_ROOT = getProjectRoot();
|
|
24
|
-
|
|
25
23
|
// ============================================================
|
|
26
24
|
// Spec Generation
|
|
27
25
|
// ============================================================
|
|
@@ -449,7 +447,7 @@ function generateRollbackPlan(taskContext) {
|
|
|
449
447
|
function saveSpec(taskId, spec) {
|
|
450
448
|
const config = getConfig();
|
|
451
449
|
const specDir = config.specificationMode?.specDirectory || '.workflow/specs';
|
|
452
|
-
const fullDir = path.join(
|
|
450
|
+
const fullDir = path.join(PATHS.root, specDir);
|
|
453
451
|
|
|
454
452
|
// Ensure directory exists
|
|
455
453
|
if (!fs.existsSync(fullDir)) {
|
|
@@ -629,7 +627,7 @@ function formatSpecAsMarkdown(spec) {
|
|
|
629
627
|
function loadSpec(taskId) {
|
|
630
628
|
const config = getConfig();
|
|
631
629
|
const specDir = config.specificationMode?.specDirectory || '.workflow/specs';
|
|
632
|
-
const jsonPath = path.join(
|
|
630
|
+
const jsonPath = path.join(PATHS.root, specDir, `${taskId}.json`);
|
|
633
631
|
|
|
634
632
|
if (!fs.existsSync(jsonPath)) {
|
|
635
633
|
return null;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* and "Let AI decide" option for intelligent defaults
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
const readline = require('node:readline');
|
|
9
|
+
const readline = require('node:readline/promises');
|
|
10
10
|
const path = require('node:path');
|
|
11
11
|
const fs = require('node:fs');
|
|
12
12
|
|
|
@@ -852,6 +852,7 @@ class EnhancedStackWizard {
|
|
|
852
852
|
|
|
853
853
|
try {
|
|
854
854
|
const generator = require('./flow-skill-generator');
|
|
855
|
+
const { PATHS } = require('./flow-utils');
|
|
855
856
|
await generator.generateSkills(technologies, this.selections);
|
|
856
857
|
|
|
857
858
|
console.log(c('green', '\n✅ Skills generated successfully!\n'));
|
|
@@ -906,12 +907,9 @@ class EnhancedStackWizard {
|
|
|
906
907
|
// INPUT HELPERS
|
|
907
908
|
// ============================================
|
|
908
909
|
|
|
909
|
-
askQuestion(prompt) {
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
resolve(answer.trim());
|
|
913
|
-
});
|
|
914
|
-
});
|
|
910
|
+
async askQuestion(prompt) {
|
|
911
|
+
const answer = await this.rl.question(prompt);
|
|
912
|
+
return answer.trim();
|
|
915
913
|
}
|
|
916
914
|
|
|
917
915
|
async askSingleChoice(question, options, defaultValue = null) {
|
|
@@ -404,9 +404,22 @@ function recordViolationPattern(learning) {
|
|
|
404
404
|
}
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
-
// Write updated content
|
|
407
|
+
// Write updated content through orchestrator
|
|
408
408
|
try {
|
|
409
|
-
|
|
409
|
+
const { writeToFeedbackPatterns } = require('./flow-learning-orchestrator');
|
|
410
|
+
const writeResult = writeToFeedbackPatterns({
|
|
411
|
+
content,
|
|
412
|
+
entryText: patternKey,
|
|
413
|
+
caller: 'flow-standards-learner/recordViolationPattern',
|
|
414
|
+
skipDedup: true // We already handle dedup via patternRegex above
|
|
415
|
+
});
|
|
416
|
+
// writeToFeedbackPatterns is async but we handle it fire-and-forget here
|
|
417
|
+
// since the sync callers can't await. The lock still protects the write.
|
|
418
|
+
if (writeResult && writeResult.then) {
|
|
419
|
+
writeResult.catch(err => {
|
|
420
|
+
if (process.env.DEBUG) console.error(`[DEBUG] recordViolationPattern write: ${err.message}`);
|
|
421
|
+
});
|
|
422
|
+
}
|
|
410
423
|
} catch (err) {
|
|
411
424
|
return { recorded: false, reason: err.message };
|
|
412
425
|
}
|
|
@@ -508,24 +521,30 @@ ${learning.ruleTemplate}
|
|
|
508
521
|
content += ruleEntry;
|
|
509
522
|
}
|
|
510
523
|
|
|
511
|
-
// Write updated content
|
|
524
|
+
// Write updated content through orchestrator
|
|
512
525
|
try {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
526
|
+
const { writeToDecisions, modifyFeedbackPatterns } = require('./flow-learning-orchestrator');
|
|
527
|
+
const writeResult = writeToDecisions({
|
|
528
|
+
content,
|
|
529
|
+
entryText: learning.patternName,
|
|
530
|
+
caller: 'flow-standards-learner/promoteToDecisions'
|
|
531
|
+
});
|
|
532
|
+
if (writeResult && writeResult.then) {
|
|
533
|
+
writeResult.catch(err => {
|
|
534
|
+
if (process.env.DEBUG) console.error(`[DEBUG] promoteToDecisions write: ${err.message}`);
|
|
535
|
+
});
|
|
536
|
+
}
|
|
517
537
|
|
|
518
|
-
|
|
519
|
-
try {
|
|
520
|
-
let patternsContent = readFile(FEEDBACK_PATTERNS_PATH, '');
|
|
538
|
+
// Update feedback-patterns.md to mark as promoted
|
|
521
539
|
const patternKey = `${learning.violationType}-${learning.patternName}`.replace(/\s+/g, '-').toLowerCase();
|
|
522
|
-
// escapeRegex prevents ReDoS from patternKey with regex metacharacters
|
|
523
540
|
const escapedPromoteKey = escapeRegex(patternKey);
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
541
|
+
modifyFeedbackPatterns((currentContent) => {
|
|
542
|
+
const updated = currentContent.replace(
|
|
543
|
+
new RegExp(`(\\|\\s*[\\d-]+\\s*\\|\\s*${escapedPromoteKey}\\s*\\|\\s*\\d+\\s*\\|)\\s*-\\s*\\|\\s*Monitor\\s*\\|`, 'i'),
|
|
544
|
+
`$1 decisions.md | **PROMOTED** |`
|
|
545
|
+
);
|
|
546
|
+
return { content: updated };
|
|
547
|
+
}, { caller: 'flow-standards-learner/promoteToDecisions-markPromoted', skipDedup: true });
|
|
529
548
|
} catch (err) {
|
|
530
549
|
// Non-fatal, rule was already promoted to decisions
|
|
531
550
|
}
|
package/scripts/flow-start.js
CHANGED
|
@@ -55,6 +55,7 @@ const assessTaskComplexity = complexityModule?.assessTaskComplexity || (() => ({
|
|
|
55
55
|
const { warnIfContextHigh } = require('./flow-context-monitor');
|
|
56
56
|
const { setCurrentTask } = require('./flow-memory-blocks');
|
|
57
57
|
const { trackTaskStart, checkAndDisplayResumeContext } = require('./flow-session-state');
|
|
58
|
+
const { setActiveTask: setHookActiveTask } = require('./flow-hook-status');
|
|
58
59
|
|
|
59
60
|
// v2.0 durable session support
|
|
60
61
|
const {
|
|
@@ -505,6 +506,7 @@ async function main() {
|
|
|
505
506
|
try {
|
|
506
507
|
trackTaskStart(taskId, taskTitle);
|
|
507
508
|
setCurrentTask(taskId, taskTitle);
|
|
509
|
+
setHookActiveTask({ id: taskId, title: taskTitle, routedAt: new Date().toISOString() });
|
|
508
510
|
} catch (err) {
|
|
509
511
|
if (process.env.DEBUG) console.error(`[DEBUG] Task tracking: ${err.message}`);
|
|
510
512
|
}
|
|
@@ -31,8 +31,8 @@ const {
|
|
|
31
31
|
// Constants
|
|
32
32
|
// ============================================================
|
|
33
33
|
|
|
34
|
-
const STATS_PATH =
|
|
35
|
-
const STATS_ARCHIVE_DIR =
|
|
34
|
+
const STATS_PATH = PATHS.modelStats;
|
|
35
|
+
const STATS_ARCHIVE_DIR = PATHS.modelStatsArchive;
|
|
36
36
|
const MAX_RECENT_TASKS = 500;
|
|
37
37
|
|
|
38
38
|
// ============================================================
|
package/scripts/flow-status.js
CHANGED
|
@@ -86,8 +86,8 @@ function collectStatus() {
|
|
|
86
86
|
status.cli.type = config.cli?.type || 'claude-code';
|
|
87
87
|
|
|
88
88
|
// Check bridge status
|
|
89
|
-
const bridgesDir =
|
|
90
|
-
const modelsDir =
|
|
89
|
+
const bridgesDir = PATHS.bridges;
|
|
90
|
+
const modelsDir = PATHS.modelsDir;
|
|
91
91
|
|
|
92
92
|
if (dirExists(bridgesDir) && dirExists(modelsDir)) {
|
|
93
93
|
status.cli.bridgeStatus = 'configured';
|
|
@@ -105,9 +105,9 @@ function collectStatus() {
|
|
|
105
105
|
if (fileExists(PATHS.config)) {
|
|
106
106
|
const config = getConfig();
|
|
107
107
|
status.config = {
|
|
108
|
-
mandatoryAfterTask: config.mandatorySteps?.afterTask
|
|
109
|
-
strictMode: config.enforcement?.strictMode
|
|
110
|
-
priorities: config.priorities
|
|
108
|
+
mandatoryAfterTask: config.mandatorySteps?.afterTask ?? [],
|
|
109
|
+
strictMode: config.enforcement?.strictMode ?? false,
|
|
110
|
+
priorities: config.priorities ?? {}
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -188,7 +188,7 @@ function main() {
|
|
|
188
188
|
if (status.git.isRepo) {
|
|
189
189
|
printSection('Git');
|
|
190
190
|
console.log(` Branch: ${status.git.branch || 'unknown'}`);
|
|
191
|
-
console.log(` Uncommitted: ${status.git.uncommitted
|
|
191
|
+
console.log(` Uncommitted: ${status.git.uncommitted ?? 0} files`);
|
|
192
192
|
console.log('');
|
|
193
193
|
}
|
|
194
194
|
|
|
@@ -229,7 +229,7 @@ function main() {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
// Show recent attempts (last 3)
|
|
232
|
-
const recentAttempts = (status.bypassTracking.attempts
|
|
232
|
+
const recentAttempts = (status.bypassTracking.attempts ?? []).slice(-3);
|
|
233
233
|
if (recentAttempts.length > 0) {
|
|
234
234
|
console.log(color('dim', ' Recent attempts:'));
|
|
235
235
|
for (const attempt of recentAttempts) {
|
|
@@ -278,7 +278,7 @@ function getRecommendation() {
|
|
|
278
278
|
const data = getReadyData();
|
|
279
279
|
|
|
280
280
|
// Check in-progress tasks first
|
|
281
|
-
const inProgress = data.inProgress
|
|
281
|
+
const inProgress = data.inProgress ?? [];
|
|
282
282
|
if (inProgress.length > 0) {
|
|
283
283
|
const task = inProgress[0];
|
|
284
284
|
const taskId = typeof task === 'string' ? task : task.id;
|
|
@@ -290,7 +290,7 @@ function getRecommendation() {
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
// Check ready tasks
|
|
293
|
-
const ready = data.ready
|
|
293
|
+
const ready = data.ready ?? [];
|
|
294
294
|
if (ready.length > 0) {
|
|
295
295
|
// Find highest priority task (P0=critical, P1=high, P2=medium, P3=low, P4=lowest)
|
|
296
296
|
const priorityOrder = { P0: 0, P1: 1, P2: 2, P3: 3, P4: 4 };
|
|
@@ -311,7 +311,7 @@ function getRecommendation() {
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
// Check blocked tasks
|
|
314
|
-
const blocked = data.blocked
|
|
314
|
+
const blocked = data.blocked ?? [];
|
|
315
315
|
if (blocked.length > 0) {
|
|
316
316
|
return {
|
|
317
317
|
action: `${blocked.length} task(s) are blocked - resolve dependencies`,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
const fs = require('node:fs');
|
|
18
18
|
const path = require('node:path');
|
|
19
19
|
const os = require('node:os');
|
|
20
|
-
const readline = require('node:readline');
|
|
20
|
+
const readline = require('node:readline/promises');
|
|
21
21
|
const { colors, printHeader, safeJsonParse } = require('./flow-utils');
|
|
22
22
|
const { success, error: errorMsg } = require('./flow-output');
|
|
23
23
|
|
|
@@ -105,7 +105,7 @@ async function interactiveSetup() {
|
|
|
105
105
|
output: process.stdout
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
const question = (q) =>
|
|
108
|
+
const question = (q) => rl.question(q);
|
|
109
109
|
|
|
110
110
|
printHeader('Status Line Setup');
|
|
111
111
|
showCurrentConfig();
|
|
@@ -9,10 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
const fs = require('node:fs');
|
|
11
11
|
const path = require('node:path');
|
|
12
|
-
const { getProjectRoot, colors, getConfig } = require('./flow-utils');
|
|
12
|
+
const { getProjectRoot, colors, getConfig, PATHS } = require('./flow-utils');
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
const CHANGELOG_PATH = path.join(PROJECT_ROOT, 'CHANGELOG.md');
|
|
14
|
+
const CHANGELOG_PATH = path.join(PATHS.root, 'CHANGELOG.md');
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
17
|
* Run update changelog step
|
|
@@ -11,96 +11,80 @@
|
|
|
11
11
|
* - Missing documentation for public APIs
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
* @param {string[]} options.files - Files modified
|
|
25
|
-
* @param {object} options.stepConfig - Step configuration
|
|
26
|
-
* @param {string} options.mode - Step mode (block/warn/prompt/auto)
|
|
27
|
-
* @returns {object} - { passed: boolean, message: string, details?: object[] }
|
|
28
|
-
*/
|
|
29
|
-
async function run(options = {}) {
|
|
30
|
-
const { files = [], stepConfig = {} } = options;
|
|
31
|
-
const flagTodo = stepConfig.flagTodo !== false;
|
|
32
|
-
const flagFixme = stepConfig.flagFixme !== false;
|
|
33
|
-
const checkJsdoc = stepConfig.checkJsdoc !== false;
|
|
34
|
-
const flagCommentedCode = stepConfig.flagCommentedCode !== false;
|
|
35
|
-
const flagStale = stepConfig.flagStale !== false;
|
|
36
|
-
|
|
37
|
-
// Filter to analyzable files
|
|
38
|
-
const analyzableExtensions = ['.js', '.ts', '.jsx', '.tsx'];
|
|
39
|
-
const analyzableFiles = files.filter(f =>
|
|
40
|
-
analyzableExtensions.some(ext => f.endsWith(ext)) &&
|
|
41
|
-
!f.includes('.d.ts')
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
if (analyzableFiles.length === 0) {
|
|
45
|
-
return { passed: true, message: 'No files to analyze' };
|
|
14
|
+
const { BaseWorkflowStep } = require('./base-workflow-step');
|
|
15
|
+
const { colors } = require('./flow-utils');
|
|
16
|
+
|
|
17
|
+
class CommentAnalyzerStep extends BaseWorkflowStep {
|
|
18
|
+
constructor() {
|
|
19
|
+
super('commentAnalyzer', {
|
|
20
|
+
extensions: ['.js', '.ts', '.jsx', '.tsx'],
|
|
21
|
+
excludeTests: false, // Comments step analyzes test files too
|
|
22
|
+
excludeDts: true,
|
|
23
|
+
});
|
|
46
24
|
}
|
|
47
25
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
26
|
+
async execute(files, options) {
|
|
27
|
+
const { stepConfig = {} } = options;
|
|
28
|
+
const flagTodo = stepConfig.flagTodo !== false;
|
|
29
|
+
const flagFixme = stepConfig.flagFixme !== false;
|
|
30
|
+
const checkJsdoc = stepConfig.checkJsdoc !== false;
|
|
31
|
+
const flagCommentedCode = stepConfig.flagCommentedCode !== false;
|
|
32
|
+
const flagStale = stepConfig.flagStale !== false;
|
|
33
|
+
|
|
34
|
+
const issues = [];
|
|
35
|
+
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
const content = this.readFile(file);
|
|
38
|
+
if (!content) continue;
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const fileIssues = analyzeComments(content, file, {
|
|
42
|
+
flagTodo,
|
|
43
|
+
flagFixme,
|
|
44
|
+
checkJsdoc,
|
|
45
|
+
flagCommentedCode,
|
|
46
|
+
flagStale,
|
|
47
|
+
});
|
|
48
|
+
issues.push(...fileIssues);
|
|
49
|
+
} catch (_err) {
|
|
50
|
+
// Skip unreadable files
|
|
51
|
+
}
|
|
66
52
|
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (issues.length === 0) {
|
|
70
|
-
return {
|
|
71
|
-
passed: true,
|
|
72
|
-
message: 'Comment analysis passed',
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
53
|
|
|
76
|
-
|
|
77
|
-
|
|
54
|
+
if (issues.length === 0) {
|
|
55
|
+
return this.pass('Comment analysis passed');
|
|
56
|
+
}
|
|
78
57
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
for (const issue of issues) {
|
|
82
|
-
if (!grouped[issue.type]) grouped[issue.type] = [];
|
|
83
|
-
grouped[issue.type].push(issue);
|
|
84
|
-
}
|
|
58
|
+
// Report issues
|
|
59
|
+
console.log(colors.yellow + '\n Comment Analysis Issues:' + colors.reset);
|
|
85
60
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
for (const issue of
|
|
89
|
-
|
|
90
|
-
|
|
61
|
+
// Group by type
|
|
62
|
+
const grouped = {};
|
|
63
|
+
for (const issue of issues) {
|
|
64
|
+
if (!grouped[issue.type]) grouped[issue.type] = [];
|
|
65
|
+
grouped[issue.type].push(issue);
|
|
91
66
|
}
|
|
92
|
-
|
|
93
|
-
|
|
67
|
+
|
|
68
|
+
for (const [type, typeIssues] of Object.entries(grouped)) {
|
|
69
|
+
console.log(colors.cyan + ` ${type}:` + colors.reset);
|
|
70
|
+
for (const issue of typeIssues.slice(0, 5)) {
|
|
71
|
+
console.log(` ${issue.file}:${issue.line}`);
|
|
72
|
+
console.log(` ${issue.message}`);
|
|
73
|
+
}
|
|
74
|
+
if (typeIssues.length > 5) {
|
|
75
|
+
console.log(colors.dim + ` ... and ${typeIssues.length - 5} more` + colors.reset);
|
|
76
|
+
}
|
|
94
77
|
}
|
|
95
|
-
}
|
|
96
78
|
|
|
97
|
-
|
|
79
|
+
const highSeverity = issues.filter(i => i.severity === 'high');
|
|
98
80
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
81
|
+
return highSeverity.length === 0
|
|
82
|
+
? this.pass(`${issues.length} comment issue(s) found (${highSeverity.length} high severity)`)
|
|
83
|
+
: this.fail(
|
|
84
|
+
`${issues.length} comment issue(s) found (${highSeverity.length} high severity)`,
|
|
85
|
+
issues
|
|
86
|
+
);
|
|
87
|
+
}
|
|
104
88
|
}
|
|
105
89
|
|
|
106
90
|
/**
|
|
@@ -303,4 +287,5 @@ function checkJsDocAccuracy(jsDocLines, functionLine, startLine, fileName) {
|
|
|
303
287
|
return issues;
|
|
304
288
|
}
|
|
305
289
|
|
|
306
|
-
|
|
290
|
+
const step = new CommentAnalyzerStep();
|
|
291
|
+
module.exports = { run: (opts) => step.run(opts), analyzeComments };
|
|
@@ -7,83 +7,62 @@
|
|
|
7
7
|
* Flags functions that exceed the configured threshold.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* @param {string[]} options.files - Files modified
|
|
21
|
-
* @param {object} options.stepConfig - Step configuration
|
|
22
|
-
* @param {string} options.mode - Step mode (block/warn/prompt/auto)
|
|
23
|
-
* @returns {object} - { passed: boolean, message: string, details?: object[] }
|
|
24
|
-
*/
|
|
25
|
-
async function run(options = {}) {
|
|
26
|
-
const { files = [], stepConfig = {}, mode } = options;
|
|
27
|
-
const threshold = stepConfig.threshold || 10;
|
|
28
|
-
|
|
29
|
-
// Filter to analyzable files
|
|
30
|
-
const analyzableExtensions = ['.js', '.ts', '.jsx', '.tsx'];
|
|
31
|
-
const analyzableFiles = files.filter(f =>
|
|
32
|
-
analyzableExtensions.some(ext => f.endsWith(ext)) &&
|
|
33
|
-
!f.includes('.test.') &&
|
|
34
|
-
!f.includes('.spec.') &&
|
|
35
|
-
!f.includes('.d.ts')
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
if (analyzableFiles.length === 0) {
|
|
39
|
-
return { passed: true, message: 'No analyzable files modified' };
|
|
10
|
+
const { BaseWorkflowStep } = require('./base-workflow-step');
|
|
11
|
+
const { colors } = require('./flow-utils');
|
|
12
|
+
|
|
13
|
+
class ComplexityStep extends BaseWorkflowStep {
|
|
14
|
+
constructor() {
|
|
15
|
+
super('codeComplexityCheck', {
|
|
16
|
+
extensions: ['.js', '.ts', '.jsx', '.tsx'],
|
|
17
|
+
excludeTests: true,
|
|
18
|
+
excludeDts: true,
|
|
19
|
+
});
|
|
40
20
|
}
|
|
41
21
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
22
|
+
async execute(files, options) {
|
|
23
|
+
const { stepConfig = {} } = options;
|
|
24
|
+
const threshold = stepConfig.threshold || 10;
|
|
25
|
+
const complexFunctions = [];
|
|
26
|
+
|
|
27
|
+
for (const file of files) {
|
|
28
|
+
const content = this.readFile(file);
|
|
29
|
+
if (!content) continue;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const functions = analyzeComplexity(content, file);
|
|
33
|
+
|
|
34
|
+
for (const func of functions) {
|
|
35
|
+
if (func.complexity > threshold) {
|
|
36
|
+
complexFunctions.push({
|
|
37
|
+
file,
|
|
38
|
+
function: func.name,
|
|
39
|
+
complexity: func.complexity,
|
|
40
|
+
line: func.line,
|
|
41
|
+
suggestion: getSuggestion(func.complexity, threshold),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
61
44
|
}
|
|
45
|
+
} catch (_err) {
|
|
46
|
+
// Skip files that can't be analyzed
|
|
62
47
|
}
|
|
63
|
-
} catch (err) {
|
|
64
|
-
// Skip files that can't be analyzed
|
|
65
48
|
}
|
|
66
|
-
}
|
|
67
49
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
message: `All functions under complexity threshold (${threshold})`,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
50
|
+
if (complexFunctions.length === 0) {
|
|
51
|
+
return this.pass(`All functions under complexity threshold (${threshold})`);
|
|
52
|
+
}
|
|
74
53
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
54
|
+
// Report complex functions
|
|
55
|
+
console.log(colors.yellow + `\n Functions exceeding complexity threshold (${threshold}):` + colors.reset);
|
|
56
|
+
for (const func of complexFunctions) {
|
|
57
|
+
console.log(` ${func.file}:${func.line} - ${func.function} (${func.complexity})`);
|
|
58
|
+
console.log(colors.gray + ` ${func.suggestion}` + colors.reset);
|
|
59
|
+
}
|
|
81
60
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
61
|
+
return this.fail(
|
|
62
|
+
`${complexFunctions.length} function(s) exceed complexity threshold`,
|
|
63
|
+
complexFunctions
|
|
64
|
+
);
|
|
65
|
+
}
|
|
87
66
|
}
|
|
88
67
|
|
|
89
68
|
/**
|
|
@@ -231,4 +210,5 @@ function getSuggestion(complexity, threshold) {
|
|
|
231
210
|
return 'Consider simplifying conditional logic';
|
|
232
211
|
}
|
|
233
212
|
|
|
234
|
-
|
|
213
|
+
const step = new ComplexityStep();
|
|
214
|
+
module.exports = { run: (opts) => step.run(opts), analyzeComplexity, calculateComplexity };
|
|
@@ -13,8 +13,6 @@ const { execSync } = require('node:child_process');
|
|
|
13
13
|
const { getProjectRoot, colors, getConfig, safeJsonParse, readJson, error } = require('./flow-utils');
|
|
14
14
|
const { getCommand, getExec } = require('./flow-script-resolver');
|
|
15
15
|
|
|
16
|
-
const PROJECT_ROOT = getProjectRoot();
|
|
17
|
-
|
|
18
16
|
// Common coverage output locations
|
|
19
17
|
const COVERAGE_PATHS = [
|
|
20
18
|
'coverage/coverage-summary.json', // Jest/Vitest JSON summary
|
|
@@ -116,7 +114,7 @@ async function run(options = {}) {
|
|
|
116
114
|
*/
|
|
117
115
|
function findExistingCoverage() {
|
|
118
116
|
for (const coveragePath of COVERAGE_PATHS) {
|
|
119
|
-
const fullPath = path.join(
|
|
117
|
+
const fullPath = path.join(PATHS.root, coveragePath);
|
|
120
118
|
if (fs.existsSync(fullPath)) {
|
|
121
119
|
try {
|
|
122
120
|
if (coveragePath.endsWith('.json')) {
|
|
@@ -142,7 +140,7 @@ function findExistingCoverage() {
|
|
|
142
140
|
*/
|
|
143
141
|
async function runCoverageTests() {
|
|
144
142
|
// Check package.json for test script
|
|
145
|
-
const packagePath = path.join(
|
|
143
|
+
const packagePath = path.join(PATHS.root, 'package.json');
|
|
146
144
|
if (!fs.existsSync(packagePath)) return null;
|
|
147
145
|
|
|
148
146
|
try {
|
|
@@ -169,7 +167,7 @@ async function runCoverageTests() {
|
|
|
169
167
|
|
|
170
168
|
// Run coverage
|
|
171
169
|
execSync(coverageCmd, {
|
|
172
|
-
cwd:
|
|
170
|
+
cwd: PATHS.root,
|
|
173
171
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
174
172
|
timeout: 300000, // 5 minute timeout
|
|
175
173
|
});
|
|
@@ -9,10 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
const fs = require('node:fs');
|
|
11
11
|
const path = require('node:path');
|
|
12
|
-
const { getProjectRoot, colors, getConfig } = require('./flow-utils');
|
|
12
|
+
const { getProjectRoot, colors, getConfig, PATHS } = require('./flow-utils');
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
const KNOWLEDGE_DIR = path.join(PROJECT_ROOT, '.claude', 'docs', 'knowledge-base');
|
|
14
|
+
const KNOWLEDGE_DIR = path.join(PATHS.root, '.claude', 'docs', 'knowledge-base');
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
17
|
* Run update knowledge base step
|