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
|
@@ -14,9 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
const fs = require('node:fs');
|
|
16
16
|
const path = require('node:path');
|
|
17
|
-
const { getProjectRoot, getConfig, color, success, warn, error } = require('./flow-utils');
|
|
18
|
-
|
|
19
|
-
const PROJECT_ROOT = getProjectRoot();
|
|
17
|
+
const { getProjectRoot, getConfig, color, success, warn, error, PATHS } = require('./flow-utils');
|
|
20
18
|
|
|
21
19
|
// ============================================================
|
|
22
20
|
// Base Scanner Class
|
|
@@ -81,7 +79,7 @@ class BaseScanner {
|
|
|
81
79
|
|
|
82
80
|
// 1. Explicit directories from config
|
|
83
81
|
for (const dir of this.config.directories) {
|
|
84
|
-
const fullPath = path.join(
|
|
82
|
+
const fullPath = path.join(PATHS.root, dir);
|
|
85
83
|
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
|
|
86
84
|
found.add(fullPath);
|
|
87
85
|
}
|
|
@@ -161,7 +159,7 @@ class BaseScanner {
|
|
|
161
159
|
}
|
|
162
160
|
};
|
|
163
161
|
|
|
164
|
-
walk(
|
|
162
|
+
walk(PATHS.root, 0, 0);
|
|
165
163
|
return results;
|
|
166
164
|
}
|
|
167
165
|
|
|
@@ -171,7 +169,7 @@ class BaseScanner {
|
|
|
171
169
|
* @returns {boolean} True if file should be excluded
|
|
172
170
|
*/
|
|
173
171
|
shouldExclude(filePath) {
|
|
174
|
-
const relativePath = path.relative(
|
|
172
|
+
const relativePath = path.relative(PATHS.root, filePath);
|
|
175
173
|
|
|
176
174
|
for (const regex of this._excludeRegexps) {
|
|
177
175
|
if (regex.test(relativePath)) {
|
|
@@ -494,5 +492,5 @@ class BaseScanner {
|
|
|
494
492
|
|
|
495
493
|
module.exports = {
|
|
496
494
|
BaseScanner,
|
|
497
|
-
PROJECT_ROOT
|
|
495
|
+
PROJECT_ROOT: PATHS.root
|
|
498
496
|
};
|
|
@@ -16,12 +16,10 @@
|
|
|
16
16
|
* const { executeScenario, generateScenario, resolveVariables, extractByPath } = require('./flow-scenario-engine');
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
'use strict';
|
|
20
|
-
|
|
21
19
|
const fs = require('node:fs');
|
|
22
20
|
const path = require('node:path');
|
|
23
21
|
const crypto = require('node:crypto');
|
|
24
|
-
const { getProjectRoot, safeJsonParseString } = require('./flow-utils');
|
|
22
|
+
const { getProjectRoot, safeJsonParseString, PATHS } = require('./flow-utils');
|
|
25
23
|
|
|
26
24
|
let verificationProfile;
|
|
27
25
|
try {
|
|
@@ -30,8 +28,6 @@ try {
|
|
|
30
28
|
verificationProfile = null;
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
const PROJECT_ROOT = getProjectRoot();
|
|
34
|
-
|
|
35
31
|
// ============================================================
|
|
36
32
|
// Constants
|
|
37
33
|
// ============================================================
|
package/scripts/flow-security.js
CHANGED
|
@@ -578,6 +578,31 @@ const CREDENTIAL_SCAN_PATTERNS = [
|
|
|
578
578
|
}
|
|
579
579
|
];
|
|
580
580
|
|
|
581
|
+
// ============================================================
|
|
582
|
+
// Shell Argument Sanitization
|
|
583
|
+
// ============================================================
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Sanitize a string for safe use in shell commands.
|
|
587
|
+
* Only allows alphanumeric, underscore, hyphen, and dot characters.
|
|
588
|
+
* @param {string} str - String to sanitize
|
|
589
|
+
* @returns {string} Sanitized string
|
|
590
|
+
*/
|
|
591
|
+
function sanitizeShellArg(str) {
|
|
592
|
+
if (!str || typeof str !== 'string') return '';
|
|
593
|
+
return str.replace(/[^a-zA-Z0-9_.-]/g, '');
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Escape a path for safe use in shell commands.
|
|
598
|
+
* @param {string} p - Path to escape
|
|
599
|
+
* @returns {string} Escaped path
|
|
600
|
+
*/
|
|
601
|
+
function escapeShellPath(p) {
|
|
602
|
+
if (!p || typeof p !== 'string') return '';
|
|
603
|
+
return p.replace(/(["\s'$`\\!*?#~<>^()[\]{}|;&])/g, '\\$1');
|
|
604
|
+
}
|
|
605
|
+
|
|
581
606
|
// ============================================================
|
|
582
607
|
// Exports
|
|
583
608
|
// ============================================================
|
|
@@ -608,6 +633,10 @@ module.exports = {
|
|
|
608
633
|
// URL/Network safety
|
|
609
634
|
isPrivateIP,
|
|
610
635
|
|
|
636
|
+
// Shell argument safety
|
|
637
|
+
sanitizeShellArg,
|
|
638
|
+
escapeShellPath,
|
|
639
|
+
|
|
611
640
|
// Constants
|
|
612
641
|
MAX_REGEX_LENGTH,
|
|
613
642
|
VALID_CODE_EXTENSIONS,
|
|
@@ -10,7 +10,7 @@ const {
|
|
|
10
10
|
execSync,
|
|
11
11
|
execFileSync,
|
|
12
12
|
spawnSync } = require('node:child_process');
|
|
13
|
-
const readline = require('node:readline');
|
|
13
|
+
const readline = require('node:readline/promises');
|
|
14
14
|
const path = require('node:path');
|
|
15
15
|
const {
|
|
16
16
|
PATHS,
|
|
@@ -22,7 +22,8 @@ const {
|
|
|
22
22
|
readFile,
|
|
23
23
|
writeFile,
|
|
24
24
|
isGitRepo,
|
|
25
|
-
getGitStatus
|
|
25
|
+
getGitStatus,
|
|
26
|
+
safeJsonParse
|
|
26
27
|
} = require('./flow-utils')
|
|
27
28
|
const { color, printSection, success, warn, error } = require('./flow-output');;
|
|
28
29
|
|
|
@@ -109,18 +110,15 @@ try {
|
|
|
109
110
|
/**
|
|
110
111
|
* Prompt user for input
|
|
111
112
|
*/
|
|
112
|
-
function prompt(question) {
|
|
113
|
+
async function prompt(question) {
|
|
113
114
|
const rl = readline.createInterface({
|
|
114
115
|
input: process.stdin,
|
|
115
116
|
output: process.stdout
|
|
116
117
|
});
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
resolve(answer);
|
|
122
|
-
});
|
|
123
|
-
});
|
|
119
|
+
const answer = await rl.question(question);
|
|
120
|
+
rl.close();
|
|
121
|
+
return answer;
|
|
124
122
|
}
|
|
125
123
|
|
|
126
124
|
/**
|
|
@@ -132,7 +130,7 @@ function checkRequirements() {
|
|
|
132
130
|
console.log(color('yellow', 'Checking session-end requirements...'));
|
|
133
131
|
|
|
134
132
|
const config = getConfig();
|
|
135
|
-
const steps = config.mandatorySteps?.onSessionEnd
|
|
133
|
+
const steps = config.mandatorySteps?.onSessionEnd ?? [];
|
|
136
134
|
|
|
137
135
|
if (steps.length > 0) {
|
|
138
136
|
console.log('Required:');
|
|
@@ -244,7 +242,7 @@ function analyzeSessionForLearnings() {
|
|
|
244
242
|
if (!sessionLearning) return;
|
|
245
243
|
|
|
246
244
|
const config = getConfig();
|
|
247
|
-
const sessionLearningConfig = config.learning?.session
|
|
245
|
+
const sessionLearningConfig = config.learning?.session ?? {};
|
|
248
246
|
|
|
249
247
|
// Check if enabled (default: true)
|
|
250
248
|
if (sessionLearningConfig.enabled === false) return;
|
|
@@ -350,7 +348,7 @@ function reviewPendingCorrections() {
|
|
|
350
348
|
|
|
351
349
|
const truncatedMsg = correction.userMessage?.length > 60
|
|
352
350
|
? correction.userMessage.slice(0, 60) + '...'
|
|
353
|
-
: correction.userMessage
|
|
351
|
+
: correction.userMessage ?? '(empty)';
|
|
354
352
|
|
|
355
353
|
console.log(` ${i + 1}. ${color(confidenceColor, `[${correction.confidence}%]`)} ${correction.correctionType || 'unknown'}`);
|
|
356
354
|
console.log(` "${truncatedMsg}"`);
|
|
@@ -407,7 +405,7 @@ function analyzeCrossSessionPatterns() {
|
|
|
407
405
|
if (!sessionLearning || !patternEnforcer) return null;
|
|
408
406
|
|
|
409
407
|
const config = getConfig();
|
|
410
|
-
const crossSessionConfig = config.learning?.crossSession
|
|
408
|
+
const crossSessionConfig = config.learning?.crossSession ?? {};
|
|
411
409
|
|
|
412
410
|
// Check if enabled (default: true)
|
|
413
411
|
if (crossSessionConfig.enabled === false) return null;
|
|
@@ -420,8 +418,8 @@ function analyzeCrossSessionPatterns() {
|
|
|
420
418
|
}
|
|
421
419
|
|
|
422
420
|
const patterns = sessionLearning.detectCrossSessionPatterns({
|
|
423
|
-
lookbackDays: crossSessionConfig.lookbackDays
|
|
424
|
-
minOccurrences: crossSessionConfig.minOccurrences
|
|
421
|
+
lookbackDays: crossSessionConfig.lookbackDays ?? 30,
|
|
422
|
+
minOccurrences: crossSessionConfig.minOccurrences ?? 3,
|
|
425
423
|
similarityThreshold: threshold
|
|
426
424
|
});
|
|
427
425
|
|
|
@@ -480,9 +478,9 @@ function saveSessionSummaryToState() {
|
|
|
480
478
|
|
|
481
479
|
// Build summary from session data
|
|
482
480
|
const summary = {
|
|
483
|
-
tasksCompleted: sessionState.metrics?.tasksCompleted
|
|
484
|
-
filesModified: sessionState.recentFiles?.slice(0, 5)
|
|
485
|
-
decisions: sessionState.recentDecisions?.map(d => d.decision).slice(0, 3)
|
|
481
|
+
tasksCompleted: sessionState.metrics?.tasksCompleted ?? 0,
|
|
482
|
+
filesModified: sessionState.recentFiles?.slice(0, 5) ?? [],
|
|
483
|
+
decisions: sessionState.recentDecisions?.map(d => d.decision).slice(0, 3) ?? [],
|
|
486
484
|
summary: memoryBlocks?.keyFacts?.slice(-3).join('; ') || 'Session ended'
|
|
487
485
|
};
|
|
488
486
|
|
|
@@ -624,7 +622,7 @@ async function automaticMemoryManagement() {
|
|
|
624
622
|
if (!memoryDb) return;
|
|
625
623
|
|
|
626
624
|
const config = getConfig();
|
|
627
|
-
const memConfig = config.memory?.automatic
|
|
625
|
+
const memConfig = config.memory?.automatic ?? {};
|
|
628
626
|
|
|
629
627
|
if (!memConfig.enabled) return;
|
|
630
628
|
|
|
@@ -635,8 +633,8 @@ async function automaticMemoryManagement() {
|
|
|
635
633
|
// 1. Apply relevance decay
|
|
636
634
|
if (memConfig.relevanceDecay?.enabled !== false) {
|
|
637
635
|
const decayResult = await memoryDb.applyRelevanceDecay({
|
|
638
|
-
decayRate: memConfig.relevanceDecay?.decayRate
|
|
639
|
-
neverAccessedPenalty: memConfig.relevanceDecay?.neverAccessedPenalty
|
|
636
|
+
decayRate: memConfig.relevanceDecay?.decayRate ?? 0.033,
|
|
637
|
+
neverAccessedPenalty: memConfig.relevanceDecay?.neverAccessedPenalty ?? 0.1
|
|
640
638
|
});
|
|
641
639
|
if (decayResult.decayed > 0) {
|
|
642
640
|
console.log(` Relevance decay: ${decayResult.decayed} facts updated`);
|
|
@@ -644,10 +642,10 @@ async function automaticMemoryManagement() {
|
|
|
644
642
|
}
|
|
645
643
|
|
|
646
644
|
// 2. Check entropy and compact if needed
|
|
647
|
-
const memoryConfig = { maxLocalFacts: config.memory?.maxLocalFacts
|
|
645
|
+
const memoryConfig = { maxLocalFacts: config.memory?.maxLocalFacts ?? 1000 };
|
|
648
646
|
const entropy = await memoryDb.getEntropyStats(memoryConfig);
|
|
649
647
|
|
|
650
|
-
const _threshold = memConfig.entropyThreshold
|
|
648
|
+
const _threshold = memConfig.entropyThreshold ?? 0.7;
|
|
651
649
|
const statusColor = entropy.status === 'healthy' ? 'green'
|
|
652
650
|
: entropy.status === 'moderate' ? 'yellow' : 'red';
|
|
653
651
|
|
|
@@ -659,7 +657,7 @@ async function automaticMemoryManagement() {
|
|
|
659
657
|
|
|
660
658
|
// Demote low-relevance facts
|
|
661
659
|
const demotion = await memoryDb.demoteToColdStorage({
|
|
662
|
-
relevanceThreshold: memConfig.demotion?.relevanceThreshold
|
|
660
|
+
relevanceThreshold: memConfig.demotion?.relevanceThreshold ?? 0.3
|
|
663
661
|
});
|
|
664
662
|
if (demotion.demoted > 0) {
|
|
665
663
|
console.log(` Demoted: ${demotion.demoted} facts`);
|
|
@@ -673,7 +671,7 @@ async function automaticMemoryManagement() {
|
|
|
673
671
|
|
|
674
672
|
// Purge old cold facts
|
|
675
673
|
const purge = await memoryDb.purgeColdFacts({
|
|
676
|
-
coldRetentionDays: memConfig.demotion?.coldRetentionDays
|
|
674
|
+
coldRetentionDays: memConfig.demotion?.coldRetentionDays ?? 90
|
|
677
675
|
});
|
|
678
676
|
if (purge.purged > 0) {
|
|
679
677
|
console.log(` Purged: ${purge.purged} old facts`);
|
|
@@ -681,11 +679,11 @@ async function automaticMemoryManagement() {
|
|
|
681
679
|
}
|
|
682
680
|
|
|
683
681
|
// 3. Check for promotion candidates and auto-promote if enabled
|
|
684
|
-
const promoConfig = config.memory?.promotion
|
|
682
|
+
const promoConfig = config.memory?.promotion ?? {};
|
|
685
683
|
if (promoConfig.enabled) {
|
|
686
684
|
const candidates = await memoryDb.getPromotionCandidates({
|
|
687
|
-
minRelevance: promoConfig.minRelevance
|
|
688
|
-
minAccessCount: promoConfig.threshold
|
|
685
|
+
minRelevance: promoConfig.minRelevance ?? 0.8,
|
|
686
|
+
minAccessCount: promoConfig.threshold ?? 3
|
|
689
687
|
});
|
|
690
688
|
|
|
691
689
|
const unpromoted = candidates.filter(c => !c.promoted_to);
|
|
@@ -752,15 +750,8 @@ async function syncRulesIfChanged() {
|
|
|
752
750
|
|
|
753
751
|
// Load last hash from state
|
|
754
752
|
const hashStatePath = path.join(STATE_DIR, 'decisions-hash.json');
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
try {
|
|
758
|
-
const hashState = JSON.parse(readFile(hashStatePath));
|
|
759
|
-
lastHash = hashState.hash;
|
|
760
|
-
} catch (_err) {
|
|
761
|
-
// Ignore parse errors
|
|
762
|
-
}
|
|
763
|
-
}
|
|
753
|
+
const hashState = safeJsonParse(hashStatePath, null);
|
|
754
|
+
const lastHash = hashState ? hashState.hash : null;
|
|
764
755
|
|
|
765
756
|
// Compare hashes
|
|
766
757
|
if (currentHash === lastHash) {
|
|
@@ -809,7 +800,7 @@ async function syncRulesIfChanged() {
|
|
|
809
800
|
*/
|
|
810
801
|
async function offerKnowledgeSync() {
|
|
811
802
|
const config = getConfig();
|
|
812
|
-
const morningConfig = config.morningBriefing
|
|
803
|
+
const morningConfig = config.morningBriefing ?? {};
|
|
813
804
|
|
|
814
805
|
// Skip if disabled or if auto-regenerate handled it in morning
|
|
815
806
|
if (morningConfig.checkKnowledgeSync === false) {
|
|
@@ -873,7 +864,7 @@ async function offerKnowledgeSync() {
|
|
|
873
864
|
*/
|
|
874
865
|
async function offerDebtCleanup() {
|
|
875
866
|
const config = getConfig();
|
|
876
|
-
const techDebtConfig = config.techDebt
|
|
867
|
+
const techDebtConfig = config.techDebt ?? {};
|
|
877
868
|
|
|
878
869
|
if (!techDebtConfig.promptOnSessionEnd) {
|
|
879
870
|
return;
|
|
@@ -996,7 +987,7 @@ async function offerDebtCleanup() {
|
|
|
996
987
|
async function cleanupStaleTasks() {
|
|
997
988
|
try {
|
|
998
989
|
const readyData = getReadyData();
|
|
999
|
-
const inProgress = readyData.inProgress
|
|
990
|
+
const inProgress = readyData.inProgress ?? [];
|
|
1000
991
|
|
|
1001
992
|
// Find auto-created tasks
|
|
1002
993
|
const autoCreatedTasks = inProgress.filter(task =>
|
|
@@ -1094,7 +1085,7 @@ async function cleanupStaleTasks() {
|
|
|
1094
1085
|
completedBy: 'session-end-cleanup'
|
|
1095
1086
|
};
|
|
1096
1087
|
|
|
1097
|
-
readyData.recentlyCompleted = readyData.recentlyCompleted
|
|
1088
|
+
readyData.recentlyCompleted = readyData.recentlyCompleted ?? [];
|
|
1098
1089
|
readyData.recentlyCompleted.unshift(completedTask);
|
|
1099
1090
|
readyData.recentlyCompleted = readyData.recentlyCompleted.slice(0, 10);
|
|
1100
1091
|
}
|
|
@@ -586,6 +586,7 @@ function applyLearnings(learnings, options = {}) {
|
|
|
586
586
|
|
|
587
587
|
/**
|
|
588
588
|
* Add learning to feedback-patterns.md
|
|
589
|
+
* Routes through the learning orchestrator for locking and dedup.
|
|
589
590
|
*/
|
|
590
591
|
function addToFeedbackPatterns(learning) {
|
|
591
592
|
if (!fileExists(FEEDBACK_PATTERNS_PATH)) {
|
|
@@ -594,45 +595,44 @@ function addToFeedbackPatterns(learning) {
|
|
|
594
595
|
}
|
|
595
596
|
|
|
596
597
|
try {
|
|
597
|
-
|
|
598
|
+
const { modifyFeedbackPatterns } = require('./flow-learning-orchestrator');
|
|
598
599
|
const today = getTodayDateString();
|
|
599
600
|
|
|
600
|
-
// Check if pattern already exists
|
|
601
|
-
if (content.includes(learning.pattern)) {
|
|
602
|
-
// Update count instead of adding duplicate
|
|
603
|
-
return true;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
// Find or create Session Analysis section
|
|
607
|
-
const sectionHeader = '## Session Analysis Patterns';
|
|
608
|
-
const tableHeader = '| Date | Pattern | Source | Count | Confidence | Status |';
|
|
609
|
-
const tableSeparator = '|------|---------|--------|-------|------------|--------|';
|
|
610
|
-
|
|
611
601
|
const newRow = `| ${today} | ${learning.pattern} | ${learning.source} | ${learning.occurrences} | ${learning.confidence}% | Monitor |`;
|
|
612
602
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const sectionEnd = content.indexOf('\n## ', content.indexOf(sectionHeader) + 1);
|
|
616
|
-
const insertPos = sectionEnd > 0 ? sectionEnd : content.length;
|
|
603
|
+
modifyFeedbackPatterns((currentContent) => {
|
|
604
|
+
let content = currentContent;
|
|
617
605
|
|
|
618
|
-
//
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
const lineEnd = content.indexOf('\n', tableEnd);
|
|
622
|
-
content = content.slice(0, lineEnd + 1) + newRow + '\n' + content.slice(lineEnd + 1);
|
|
606
|
+
// Check if pattern already exists
|
|
607
|
+
if (content.includes(learning.pattern)) {
|
|
608
|
+
return null; // Signal no write needed (already exists)
|
|
623
609
|
}
|
|
624
|
-
} else {
|
|
625
|
-
// Add new section before "## Promotion History" or at end
|
|
626
|
-
const newSection = `\n---\n\n${sectionHeader}\n\n${tableHeader}\n${tableSeparator}\n${newRow}\n`;
|
|
627
610
|
|
|
628
|
-
|
|
629
|
-
|
|
611
|
+
// Find or create Session Analysis section
|
|
612
|
+
const sectionHeader = '## Session Analysis Patterns';
|
|
613
|
+
const tableHeader = '| Date | Pattern | Source | Count | Confidence | Status |';
|
|
614
|
+
const tableSeparator = '|------|---------|--------|-------|------------|--------|';
|
|
615
|
+
|
|
616
|
+
if (content.includes(sectionHeader)) {
|
|
617
|
+
const sectionEnd = content.indexOf('\n## ', content.indexOf(sectionHeader) + 1);
|
|
618
|
+
const insertPos = sectionEnd > 0 ? sectionEnd : content.length;
|
|
619
|
+
const tableEnd = content.lastIndexOf('|', insertPos);
|
|
620
|
+
if (tableEnd > content.indexOf(sectionHeader)) {
|
|
621
|
+
const lineEnd = content.indexOf('\n', tableEnd);
|
|
622
|
+
content = content.slice(0, lineEnd + 1) + newRow + '\n' + content.slice(lineEnd + 1);
|
|
623
|
+
}
|
|
630
624
|
} else {
|
|
631
|
-
|
|
625
|
+
const newSection = `\n---\n\n${sectionHeader}\n\n${tableHeader}\n${tableSeparator}\n${newRow}\n`;
|
|
626
|
+
if (content.includes('## Promotion History')) {
|
|
627
|
+
content = content.replace('## Promotion History', newSection + '\n## Promotion History');
|
|
628
|
+
} else {
|
|
629
|
+
content = content.trimEnd() + newSection;
|
|
630
|
+
}
|
|
632
631
|
}
|
|
633
|
-
}
|
|
634
632
|
|
|
635
|
-
|
|
633
|
+
return { content, entryText: learning.pattern };
|
|
634
|
+
}, { caller: 'flow-session-learning/addToFeedbackPatterns' });
|
|
635
|
+
|
|
636
636
|
return true;
|
|
637
637
|
} catch (err) {
|
|
638
638
|
if (process.env.DEBUG) console.error(`[DEBUG] addToFeedbackPatterns: ${err.message}`);
|
|
@@ -642,6 +642,7 @@ function addToFeedbackPatterns(learning) {
|
|
|
642
642
|
|
|
643
643
|
/**
|
|
644
644
|
* Add high-confidence learning to decisions.md
|
|
645
|
+
* Routes through the learning orchestrator for locking and dedup.
|
|
645
646
|
*/
|
|
646
647
|
function addToDecisions(learning) {
|
|
647
648
|
if (!fileExists(DECISIONS_PATH)) {
|
|
@@ -650,16 +651,19 @@ function addToDecisions(learning) {
|
|
|
650
651
|
}
|
|
651
652
|
|
|
652
653
|
try {
|
|
653
|
-
|
|
654
|
+
const { modifyDecisions } = require('./flow-learning-orchestrator');
|
|
654
655
|
const today = getTodayDateString();
|
|
655
656
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
return true; // Already exists
|
|
659
|
-
}
|
|
657
|
+
modifyDecisions((currentContent) => {
|
|
658
|
+
let content = currentContent;
|
|
660
659
|
|
|
661
|
-
|
|
662
|
-
|
|
660
|
+
// Check if pattern already exists
|
|
661
|
+
if (content.includes(learning.pattern)) {
|
|
662
|
+
return null; // Already exists, no write needed
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Create decision entry
|
|
666
|
+
const decisionEntry = `
|
|
663
667
|
### ${learning.pattern} (${today})
|
|
664
668
|
<!-- PIN: ${learning.pattern} -->
|
|
665
669
|
|
|
@@ -670,21 +674,21 @@ ${learning.description}
|
|
|
670
674
|
**Recommendation**: ${learning.recommendation}
|
|
671
675
|
`;
|
|
672
676
|
|
|
673
|
-
|
|
674
|
-
|
|
677
|
+
// Find Session Learnings section or create one
|
|
678
|
+
const sectionHeader = '## Session-Learned Patterns';
|
|
675
679
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
680
|
+
if (content.includes(sectionHeader)) {
|
|
681
|
+
const sectionIndex = content.indexOf(sectionHeader);
|
|
682
|
+
const nextSection = content.indexOf('\n## ', sectionIndex + sectionHeader.length);
|
|
683
|
+
const insertPos = nextSection > 0 ? nextSection : content.length;
|
|
684
|
+
content = content.slice(0, insertPos) + decisionEntry + content.slice(insertPos);
|
|
685
|
+
} else {
|
|
686
|
+
content = content.trimEnd() + '\n\n---\n\n' + sectionHeader + '\n' + decisionEntry;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return { content, entryText: learning.pattern };
|
|
690
|
+
}, { caller: 'flow-session-learning/addToDecisions' });
|
|
686
691
|
|
|
687
|
-
fs.writeFileSync(DECISIONS_PATH, content, 'utf-8');
|
|
688
692
|
return true;
|
|
689
693
|
} catch (err) {
|
|
690
694
|
if (process.env.DEBUG) console.error(`[DEBUG] addToDecisions: ${err.message}`);
|
|
@@ -17,12 +17,11 @@ const fs = require('node:fs');
|
|
|
17
17
|
const path = require('node:path');
|
|
18
18
|
const {
|
|
19
19
|
getProjectRoot,
|
|
20
|
-
getConfig
|
|
20
|
+
getConfig, PATHS
|
|
21
21
|
} = require('./flow-utils')
|
|
22
22
|
const { color, success, warn, error } = require('./flow-output');;
|
|
23
23
|
|
|
24
|
-
const
|
|
25
|
-
const GIT_DIR = path.join(PROJECT_ROOT, '.git');
|
|
24
|
+
const GIT_DIR = path.join(PATHS.root, '.git');
|
|
26
25
|
const HOOKS_DIR = path.join(GIT_DIR, 'hooks');
|
|
27
26
|
|
|
28
27
|
// Hook marker to identify our managed hooks
|
|
@@ -13,13 +13,12 @@
|
|
|
13
13
|
|
|
14
14
|
const fs = require('node:fs');
|
|
15
15
|
const path = require('node:path');
|
|
16
|
-
const readline = require('node:readline');
|
|
17
|
-
const { getProjectRoot, colors } = require('./flow-utils');
|
|
16
|
+
const readline = require('node:readline/promises');
|
|
17
|
+
const { getProjectRoot, colors, PATHS } = require('./flow-utils');
|
|
18
18
|
const { success, warn, error: errorMsg, info, print } = require('./flow-output');
|
|
19
19
|
const { getAllSkills, getSkillDir } = require('./flow-skill-matcher');
|
|
20
20
|
|
|
21
|
-
const
|
|
22
|
-
const SKILLS_DIR = path.join(PROJECT_ROOT, '.claude', 'skills');
|
|
21
|
+
const SKILLS_DIR = path.join(PATHS.root, '.claude', 'skills');
|
|
23
22
|
const TEMPLATE_DIR = path.join(SKILLS_DIR, '_template');
|
|
24
23
|
|
|
25
24
|
function log(color, ...args) {
|
|
@@ -33,13 +32,9 @@ async function prompt(question, defaultValue = '') {
|
|
|
33
32
|
});
|
|
34
33
|
|
|
35
34
|
const defaultHint = defaultValue ? ` (${defaultValue})` : '';
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
rl.close();
|
|
40
|
-
resolve(answer.trim() || defaultValue);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
35
|
+
const answer = await rl.question(`${question}${defaultHint}: `);
|
|
36
|
+
rl.close();
|
|
37
|
+
return answer.trim() || defaultValue;
|
|
43
38
|
}
|
|
44
39
|
|
|
45
40
|
async function confirm(question) {
|
|
@@ -187,7 +182,7 @@ _Add key patterns here._
|
|
|
187
182
|
}
|
|
188
183
|
|
|
189
184
|
async function createFromPatterns() {
|
|
190
|
-
const feedbackPath =
|
|
185
|
+
const feedbackPath = PATHS.feedbackPatterns;
|
|
191
186
|
|
|
192
187
|
if (!fs.existsSync(feedbackPath)) {
|
|
193
188
|
warn('No feedback-patterns.md found');
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const fs = require('node:fs');
|
|
9
9
|
const path = require('node:path');
|
|
10
|
-
const { ensureDir, getConfig, invalidateConfigCache, writeJson, PATHS, success } = require('./flow-utils');
|
|
10
|
+
const { ensureDir, getConfig, invalidateConfigCache, writeJson, PATHS, success, safeJsonParse } = require('./flow-utils');
|
|
11
11
|
const { getTodayDate } = require('./flow-output');
|
|
12
12
|
|
|
13
13
|
// Import helper functions from tech options
|
|
@@ -890,10 +890,14 @@ function updateDecisionsMd(selections, technologies, projectRoot) {
|
|
|
890
890
|
// Append tech stack section
|
|
891
891
|
content = content.trimEnd() + '\n' + techStackSection;
|
|
892
892
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
893
|
+
// Route through orchestrator for locking and dedup
|
|
894
|
+
try {
|
|
895
|
+
const { writeToDecisions } = require('./flow-learning-orchestrator');
|
|
896
|
+
writeToDecisions({ content, entryText: 'Tech Stack (auto-generated)', caller: 'flow-skill-generator/updateDecisions', skipDedup: true, syncRules: true }).catch(() => {});
|
|
897
|
+
} catch (_err) {
|
|
898
|
+
fs.writeFileSync(decisionsPath, content, 'utf8');
|
|
899
|
+
syncDecisionsToRules();
|
|
900
|
+
}
|
|
897
901
|
}
|
|
898
902
|
|
|
899
903
|
function updateConfigJson(technologies, _projectRoot) {
|
|
@@ -1466,17 +1470,9 @@ For manual use, run the wizard first: node flow-stack-wizard.js
|
|
|
1466
1470
|
process.exit(1);
|
|
1467
1471
|
}
|
|
1468
1472
|
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
const parsed = JSON.parse(content);
|
|
1473
|
-
if (typeof parsed !== 'object' || parsed === null) {
|
|
1474
|
-
console.error('stack-selections.json is not a valid object');
|
|
1475
|
-
process.exit(1);
|
|
1476
|
-
}
|
|
1477
|
-
selections = parsed;
|
|
1478
|
-
} catch (err) {
|
|
1479
|
-
console.error(`Failed to parse stack-selections.json: ${err.message}`);
|
|
1473
|
+
const selections = safeJsonParse(selectionsPath, null);
|
|
1474
|
+
if (!selections) {
|
|
1475
|
+
console.error('stack-selections.json is not a valid object or could not be parsed');
|
|
1480
1476
|
process.exit(1);
|
|
1481
1477
|
}
|
|
1482
1478
|
const { collectTechnologiesFromSelections } = require('./flow-tech-options');
|
|
@@ -20,13 +20,10 @@
|
|
|
20
20
|
const fs = require('node:fs');
|
|
21
21
|
const path = require('node:path');
|
|
22
22
|
const { execSync } = require('node:child_process');
|
|
23
|
-
const { getProjectRoot, getConfig, colors } = require('./flow-utils');
|
|
23
|
+
const { getProjectRoot, getConfig, colors, PATHS } = require('./flow-utils');
|
|
24
24
|
const { getAllSkills, getSkillDir } = require('./flow-skill-matcher');
|
|
25
25
|
|
|
26
|
-
const
|
|
27
|
-
const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
|
|
28
|
-
const SKILLS_DIR = path.join(PROJECT_ROOT, '.claude', 'skills');
|
|
29
|
-
const STATE_DIR = path.join(WORKFLOW_DIR, 'state');
|
|
26
|
+
const SKILLS_DIR = path.join(PATHS.root, '.claude', 'skills');
|
|
30
27
|
|
|
31
28
|
function log(color, ...args) {
|
|
32
29
|
console.log(colors[color] + args.join(' ') + colors.reset);
|
|
@@ -582,7 +579,7 @@ function getFileSignature(files) {
|
|
|
582
579
|
* Log unmatched files to feedback-patterns.md with proper count tracking
|
|
583
580
|
*/
|
|
584
581
|
function logToFeedbackPatterns(context, unmatchedFiles) {
|
|
585
|
-
const feedbackPath =
|
|
582
|
+
const feedbackPath = PATHS.feedbackPatterns;
|
|
586
583
|
|
|
587
584
|
if (!fs.existsSync(feedbackPath)) return;
|
|
588
585
|
|
|
@@ -641,7 +638,19 @@ function logToFeedbackPatterns(context, unmatchedFiles) {
|
|
|
641
638
|
content = content.slice(0, headerEnd) + entry + '\n' + content.slice(headerEnd);
|
|
642
639
|
}
|
|
643
640
|
|
|
644
|
-
|
|
641
|
+
try {
|
|
642
|
+
const { writeToFeedbackPatterns } = require('./flow-learning-orchestrator');
|
|
643
|
+
writeToFeedbackPatterns({
|
|
644
|
+
content,
|
|
645
|
+
entryText: `Files changed with no matching skill: ${filesPreview}`,
|
|
646
|
+
caller: 'flow-skill-learn/logToFeedbackPatterns',
|
|
647
|
+
skipDedup: true // We already handle dedup above via foundExisting check
|
|
648
|
+
}).catch(_err => {
|
|
649
|
+
if (process.env.DEBUG) console.error(`[DEBUG] logToFeedbackPatterns: ${_err.message}`);
|
|
650
|
+
});
|
|
651
|
+
} catch (_err) {
|
|
652
|
+
if (process.env.DEBUG) console.error(`[DEBUG] logToFeedbackPatterns: ${_err.message}`);
|
|
653
|
+
}
|
|
645
654
|
}
|
|
646
655
|
|
|
647
656
|
// ============================================================
|
|
@@ -814,7 +823,7 @@ module.exports = {
|
|
|
814
823
|
};
|
|
815
824
|
|
|
816
825
|
if (require.main === module) {
|
|
817
|
-
main().catch(
|
|
826
|
+
main().catch(err => {
|
|
818
827
|
log('red', `Error: ${err.message}`);
|
|
819
828
|
process.exit(1);
|
|
820
829
|
});
|
|
@@ -31,8 +31,7 @@ function getSkillFilePath(dir) {
|
|
|
31
31
|
const { getProjectRoot, getConfig, PATHS, colors } = require('./flow-utils');
|
|
32
32
|
const { error: errorMsg } = require('./flow-output');
|
|
33
33
|
|
|
34
|
-
const
|
|
35
|
-
const SKILLS_DIR = path.join(PROJECT_ROOT, '.claude', 'skills');
|
|
34
|
+
const SKILLS_DIR = path.join(PATHS.root, '.claude', 'skills');
|
|
36
35
|
|
|
37
36
|
// Maximum nesting depth for skill directories (prevents runaway recursion)
|
|
38
37
|
const MAX_SKILL_NESTING_DEPTH = 3;
|