wogiflow 1.0.12 → 1.0.13
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/.workflow/specs/architecture.md.template +24 -0
- package/.workflow/specs/stack.md.template +33 -0
- package/.workflow/specs/testing.md.template +36 -0
- package/README.md +90 -1
- package/package.json +1 -1
- package/scripts/MEMORY-ARCHITECTURE.md +150 -0
- package/scripts/flow +20 -19
- package/scripts/flow-auto-context.js +97 -3
- package/scripts/flow-conflict-resolver.js +735 -0
- package/scripts/flow-context-gatherer.js +520 -0
- package/scripts/flow-context-monitor.js +148 -19
- package/scripts/flow-damage-control.js +5 -1
- package/scripts/flow-export-profile +168 -1
- package/scripts/flow-import-profile +257 -6
- package/scripts/flow-instruction-richness.js +182 -18
- package/scripts/flow-knowledge-router.js +2 -0
- package/scripts/flow-knowledge-sync.js +2 -0
- package/scripts/{flow-transcript-chunking.js → flow-long-input-chunking.js} +4 -2
- package/scripts/{flow-transcript-parsing.js → flow-long-input-parsing.js} +35 -0
- package/scripts/{flow-transcript-stories.js → flow-long-input-stories.js} +86 -38
- package/scripts/{flow-transcript-digest.js → flow-long-input.js} +231 -15
- package/scripts/flow-memory-db.js +386 -1
- package/scripts/flow-memory-sync.js +2 -0
- package/scripts/flow-model-adapter.js +53 -29
- package/scripts/flow-model-router.js +246 -1
- package/scripts/flow-morning.js +94 -0
- package/scripts/flow-onboard +223 -10
- package/scripts/flow-orchestrate-validation.js +539 -0
- package/scripts/flow-orchestrate.js +16 -507
- package/scripts/flow-pattern-extractor.js +1265 -0
- package/scripts/flow-prompt-composer.js +222 -2
- package/scripts/flow-quality-guard.js +594 -0
- package/scripts/flow-section-index.js +713 -0
- package/scripts/flow-section-resolver.js +484 -0
- package/scripts/flow-session-end.js +188 -2
- package/scripts/flow-skill-create.js +19 -3
- package/scripts/flow-skill-matcher.js +122 -7
- package/scripts/flow-statusline-setup.js +218 -0
- package/scripts/flow-step-review.js +19 -0
- package/scripts/flow-tech-debt.js +734 -0
- package/scripts/flow-utils.js +2 -0
- package/scripts/hooks/core/long-input-gate.js +293 -0
- package/scripts/flow-parallel-detector.js +0 -399
- package/scripts/flow-parallel-dispatch.js +0 -987
- /package/scripts/{flow-transcript-language.js → flow-long-input-language.js} +0 -0
|
@@ -22,7 +22,8 @@ const {
|
|
|
22
22
|
success,
|
|
23
23
|
readFile,
|
|
24
24
|
fileExists,
|
|
25
|
-
printHeader
|
|
25
|
+
printHeader,
|
|
26
|
+
safeJsonParse
|
|
26
27
|
} = require('./flow-utils');
|
|
27
28
|
|
|
28
29
|
// ============================================================
|
|
@@ -134,9 +135,71 @@ const DEFAULTS = {
|
|
|
134
135
|
criticalAt: 0.85, // 85% - critical threshold
|
|
135
136
|
contextWindow: 200000, // Claude's context window
|
|
136
137
|
checkOnSessionStart: true,
|
|
137
|
-
checkAfterTask: true
|
|
138
|
+
checkAfterTask: true,
|
|
139
|
+
// Tracking method: 'estimated' (default), 'native', or 'auto'
|
|
140
|
+
// - 'estimated': Uses token estimation from state files
|
|
141
|
+
// - 'native': Uses Claude Code's native tracking (v1.0.52+)
|
|
142
|
+
// - 'auto': Uses native if available, falls back to estimated
|
|
143
|
+
trackingMethod: 'auto'
|
|
138
144
|
};
|
|
139
145
|
|
|
146
|
+
// Path to native context info (written by Claude Code hooks if configured)
|
|
147
|
+
const NATIVE_CONTEXT_FILE = path.join(STATE_DIR, 'context-info.json');
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Try to read native context info from Claude Code
|
|
151
|
+
* Returns null if not available
|
|
152
|
+
*/
|
|
153
|
+
function getNativeContextInfo() {
|
|
154
|
+
if (!fs.existsSync(NATIVE_CONTEXT_FILE)) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Use safeJsonParse for prototype pollution protection
|
|
159
|
+
const data = safeJsonParse(NATIVE_CONTEXT_FILE, null);
|
|
160
|
+
if (!data) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check if data is recent (within last 5 minutes)
|
|
165
|
+
if (data.timestamp) {
|
|
166
|
+
const age = Date.now() - new Date(data.timestamp).getTime();
|
|
167
|
+
if (age > 5 * 60 * 1000) {
|
|
168
|
+
return null; // Data too old
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Validate required fields
|
|
173
|
+
if (typeof data.usedPercentage === 'number') {
|
|
174
|
+
return {
|
|
175
|
+
usedPercentage: data.usedPercentage,
|
|
176
|
+
remainingPercentage: data.remainingPercentage || (100 - data.usedPercentage),
|
|
177
|
+
timestamp: data.timestamp,
|
|
178
|
+
source: 'native'
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Write native context info (called from hooks)
|
|
187
|
+
*/
|
|
188
|
+
function writeNativeContextInfo(usedPercentage, remainingPercentage) {
|
|
189
|
+
try {
|
|
190
|
+
const data = {
|
|
191
|
+
usedPercentage,
|
|
192
|
+
remainingPercentage,
|
|
193
|
+
timestamp: new Date().toISOString(),
|
|
194
|
+
source: 'claude-code'
|
|
195
|
+
};
|
|
196
|
+
fs.writeFileSync(NATIVE_CONTEXT_FILE, JSON.stringify(data, null, 2));
|
|
197
|
+
return true;
|
|
198
|
+
} catch (err) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
140
203
|
/**
|
|
141
204
|
* Get context monitor configuration
|
|
142
205
|
*/
|
|
@@ -150,6 +213,7 @@ function getContextMonitorConfig() {
|
|
|
150
213
|
|
|
151
214
|
/**
|
|
152
215
|
* Check context health and return status
|
|
216
|
+
* Supports both native Claude Code tracking and estimated tracking
|
|
153
217
|
*/
|
|
154
218
|
function checkContextHealth() {
|
|
155
219
|
const config = getContextMonitorConfig();
|
|
@@ -160,21 +224,57 @@ function checkContextHealth() {
|
|
|
160
224
|
currentTokens: 0,
|
|
161
225
|
contextWindow: config.contextWindow,
|
|
162
226
|
usage: 0,
|
|
163
|
-
recommendation: null
|
|
227
|
+
recommendation: null,
|
|
228
|
+
trackingMethod: 'disabled'
|
|
164
229
|
};
|
|
165
230
|
}
|
|
166
231
|
|
|
167
|
-
|
|
168
|
-
const
|
|
232
|
+
// Determine tracking method
|
|
233
|
+
const trackingMethod = config.trackingMethod || 'auto';
|
|
234
|
+
let usage, total, breakdown, trackingSource;
|
|
235
|
+
|
|
236
|
+
// Try native tracking first (if configured)
|
|
237
|
+
if (trackingMethod === 'native' || trackingMethod === 'auto') {
|
|
238
|
+
const nativeInfo = getNativeContextInfo();
|
|
239
|
+
if (nativeInfo) {
|
|
240
|
+
usage = nativeInfo.usedPercentage / 100;
|
|
241
|
+
total = Math.round(usage * config.contextWindow);
|
|
242
|
+
breakdown = { 'native-tracking': total };
|
|
243
|
+
trackingSource = 'native';
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Fall back to estimated tracking
|
|
248
|
+
if (!trackingSource && (trackingMethod === 'estimated' || trackingMethod === 'auto')) {
|
|
249
|
+
const contextData = getContextBreakdown();
|
|
250
|
+
breakdown = contextData.breakdown;
|
|
251
|
+
total = contextData.total;
|
|
252
|
+
usage = total / config.contextWindow;
|
|
253
|
+
trackingSource = 'estimated';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// If native was required but not available
|
|
257
|
+
if (!trackingSource && trackingMethod === 'native') {
|
|
258
|
+
return {
|
|
259
|
+
status: 'unavailable',
|
|
260
|
+
currentTokens: 0,
|
|
261
|
+
contextWindow: config.contextWindow,
|
|
262
|
+
usage: 0,
|
|
263
|
+
usagePercent: 0,
|
|
264
|
+
recommendation: 'Native tracking not available. Run status line setup or switch to "estimated" mode.',
|
|
265
|
+
trackingMethod: 'native',
|
|
266
|
+
trackingSource: null
|
|
267
|
+
};
|
|
268
|
+
}
|
|
169
269
|
|
|
170
270
|
let status, recommendation;
|
|
171
271
|
|
|
172
272
|
if (usage >= config.criticalAt) {
|
|
173
273
|
status = 'critical';
|
|
174
|
-
recommendation = 'Run /compact NOW to avoid context overflow';
|
|
274
|
+
recommendation = 'Run /wogi-compact NOW to avoid context overflow';
|
|
175
275
|
} else if (usage >= config.warnAt) {
|
|
176
276
|
status = 'warning';
|
|
177
|
-
recommendation = 'Consider running /compact soon';
|
|
277
|
+
recommendation = 'Consider running /wogi-compact soon';
|
|
178
278
|
} else {
|
|
179
279
|
status = 'healthy';
|
|
180
280
|
recommendation = null;
|
|
@@ -191,7 +291,9 @@ function checkContextHealth() {
|
|
|
191
291
|
thresholds: {
|
|
192
292
|
warn: config.warnAt,
|
|
193
293
|
critical: config.criticalAt
|
|
194
|
-
}
|
|
294
|
+
},
|
|
295
|
+
trackingMethod,
|
|
296
|
+
trackingSource
|
|
195
297
|
};
|
|
196
298
|
}
|
|
197
299
|
|
|
@@ -241,12 +343,29 @@ function showContextBreakdown() {
|
|
|
241
343
|
healthy: 'green',
|
|
242
344
|
warning: 'yellow',
|
|
243
345
|
critical: 'red',
|
|
244
|
-
disabled: 'dim'
|
|
346
|
+
disabled: 'dim',
|
|
347
|
+
unavailable: 'yellow'
|
|
245
348
|
};
|
|
246
349
|
const statusColor = statusColors[health.status] || 'white';
|
|
247
350
|
console.log(`Status: ${color(statusColor, health.status.toUpperCase())}`);
|
|
351
|
+
|
|
352
|
+
// Show tracking method
|
|
353
|
+
if (health.trackingSource) {
|
|
354
|
+
const sourceLabel = health.trackingSource === 'native' ? 'Claude Code Native' : 'Estimated';
|
|
355
|
+
console.log(`Tracking: ${color('dim', sourceLabel)}`);
|
|
356
|
+
}
|
|
248
357
|
console.log('');
|
|
249
358
|
|
|
359
|
+
// Handle unavailable status
|
|
360
|
+
if (health.status === 'unavailable') {
|
|
361
|
+
console.log(color('yellow', 'Native tracking is configured but not available.'));
|
|
362
|
+
console.log(color('dim', 'Options:'));
|
|
363
|
+
console.log(color('dim', ' 1. Run /wogi-statusline-setup to configure status line'));
|
|
364
|
+
console.log(color('dim', ' 2. Set trackingMethod: "estimated" in config.json'));
|
|
365
|
+
console.log(color('dim', ' 3. Set trackingMethod: "auto" to auto-fallback'));
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
250
369
|
// Progress bar
|
|
251
370
|
const barWidth = 40;
|
|
252
371
|
const filled = Math.min(Math.round(health.usage * barWidth), barWidth);
|
|
@@ -255,18 +374,23 @@ function showContextBreakdown() {
|
|
|
255
374
|
console.log(`${health.currentTokens.toLocaleString()} / ${health.contextWindow.toLocaleString()} tokens`);
|
|
256
375
|
console.log('');
|
|
257
376
|
|
|
258
|
-
// Breakdown
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
377
|
+
// Breakdown (only show for estimated tracking)
|
|
378
|
+
if (health.trackingSource === 'estimated') {
|
|
379
|
+
console.log(color('cyan', 'Breakdown:'));
|
|
380
|
+
const sortedBreakdown = Object.entries(health.breakdown)
|
|
381
|
+
.sort((a, b) => b[1] - a[1]);
|
|
262
382
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
383
|
+
for (const [file, tokens] of sortedBreakdown) {
|
|
384
|
+
if (tokens > 0) {
|
|
385
|
+
const percent = Math.round((tokens / health.currentTokens) * 100);
|
|
386
|
+
console.log(` ${file.padEnd(25)} ${tokens.toLocaleString().padStart(8)} tokens (${percent}%)`);
|
|
387
|
+
}
|
|
267
388
|
}
|
|
389
|
+
console.log('');
|
|
390
|
+
} else if (health.trackingSource === 'native') {
|
|
391
|
+
console.log(color('dim', '(Native tracking - breakdown not available)'));
|
|
392
|
+
console.log('');
|
|
268
393
|
}
|
|
269
|
-
console.log('');
|
|
270
394
|
|
|
271
395
|
// Thresholds
|
|
272
396
|
console.log(color('dim', `Thresholds: warn=${Math.round(health.thresholds.warn * 100)}%, critical=${Math.round(health.thresholds.critical * 100)}%`));
|
|
@@ -374,11 +498,16 @@ module.exports = {
|
|
|
374
498
|
checkContextHealth,
|
|
375
499
|
getContextMonitorConfig,
|
|
376
500
|
|
|
501
|
+
// Native tracking
|
|
502
|
+
getNativeContextInfo,
|
|
503
|
+
writeNativeContextInfo,
|
|
504
|
+
|
|
377
505
|
// Warnings
|
|
378
506
|
warnIfContextHigh,
|
|
379
507
|
showContextBreakdown,
|
|
380
508
|
getStatusLine,
|
|
381
509
|
|
|
382
510
|
// Constants
|
|
383
|
-
DEFAULTS
|
|
511
|
+
DEFAULTS,
|
|
512
|
+
NATIVE_CONTEXT_FILE
|
|
384
513
|
};
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Inspired by Hookify plugin patterns, adapted for multi-CLI compatibility.
|
|
10
10
|
*
|
|
11
|
+
* LIMITATION: The 'prompt' event type's AI analysis hook is not yet implemented.
|
|
12
|
+
* Currently returns 'allow' for all prompts. See evaluatePromptWithAI() around line 734.
|
|
13
|
+
* TODO: Integrate with an AI API for prompt safety evaluation.
|
|
14
|
+
*
|
|
11
15
|
* Usage:
|
|
12
16
|
* flow damage-control check "<command>" Check if command is allowed
|
|
13
17
|
* flow damage-control event <type> <ctx> Check event against rules
|
|
@@ -368,7 +372,7 @@ function loadPatterns() {
|
|
|
368
372
|
parsed.rules = parsed.rules || [];
|
|
369
373
|
return parsed;
|
|
370
374
|
} catch (err) {
|
|
371
|
-
console.error(
|
|
375
|
+
console.error('Error loading damage-control.yaml:', err.message);
|
|
372
376
|
return {
|
|
373
377
|
rules: [],
|
|
374
378
|
blocked: [],
|
|
@@ -26,7 +26,13 @@ show_help() {
|
|
|
26
26
|
echo " --rules Include .claude/rules/ and decisions.md"
|
|
27
27
|
echo " --learnings Include feedback-patterns.md and skill learnings"
|
|
28
28
|
echo " --templates Include sanitized project.md and roadmap templates"
|
|
29
|
-
echo " --full Include everything (all categories)"
|
|
29
|
+
echo " --full Include everything (all categories + patterns)"
|
|
30
|
+
echo ""
|
|
31
|
+
echo "Pattern Extraction (NEW):"
|
|
32
|
+
echo " --extract-patterns Scan codebase and extract patterns"
|
|
33
|
+
echo " --resolve-conflicts Interactive conflict resolution (with --extract-patterns)"
|
|
34
|
+
echo " --analysis-mode MODE Analysis depth: balanced (default), deep"
|
|
35
|
+
echo " --include-examples Include code snippets as examples"
|
|
30
36
|
echo ""
|
|
31
37
|
echo "Legacy options:"
|
|
32
38
|
echo " --include-decisions Include decisions.md only"
|
|
@@ -34,6 +40,7 @@ show_help() {
|
|
|
34
40
|
echo ""
|
|
35
41
|
echo "Examples:"
|
|
36
42
|
echo " flow export-profile my-team --full"
|
|
43
|
+
echo " flow export-profile my-team --extract-patterns --resolve-conflicts"
|
|
37
44
|
echo " flow export-profile my-team --rules --learnings"
|
|
38
45
|
echo " flow export-profile my-team --include-decisions"
|
|
39
46
|
}
|
|
@@ -46,12 +53,56 @@ fi
|
|
|
46
53
|
PROFILE_NAME="$1"
|
|
47
54
|
shift
|
|
48
55
|
|
|
56
|
+
# Validate profile name to prevent path traversal
|
|
57
|
+
if ! [[ "$PROFILE_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
|
58
|
+
echo -e "${RED}Error: Invalid profile name. Use only letters, numbers, underscores, and hyphens.${NC}"
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Safe JSON reading function (prevents code injection by using process.argv)
|
|
63
|
+
safe_read_json() {
|
|
64
|
+
local file="$1"
|
|
65
|
+
local query="$2"
|
|
66
|
+
node -e '
|
|
67
|
+
const fs = require("fs");
|
|
68
|
+
const file = process.argv[1];
|
|
69
|
+
const query = process.argv[2];
|
|
70
|
+
try {
|
|
71
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
72
|
+
// Check for prototype pollution attempts
|
|
73
|
+
if (/__proto__|constructor\s*["'"'"'`:]|prototype\s*["'"'"'`:]/i.test(content)) {
|
|
74
|
+
console.error("Suspicious content detected");
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
const p = JSON.parse(content);
|
|
78
|
+
// Safe property access based on query type
|
|
79
|
+
if (query === "conflicts.length") {
|
|
80
|
+
console.log(p.conflicts ? p.conflicts.length : 0);
|
|
81
|
+
} else if (query === "conflicts.json") {
|
|
82
|
+
console.log(JSON.stringify(p.conflicts, null, 2));
|
|
83
|
+
} else if (query === "recommendations.json") {
|
|
84
|
+
console.log(JSON.stringify(p.recommendations, null, 2));
|
|
85
|
+
} else {
|
|
86
|
+
console.error("Unknown query: " + query);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
} catch(e) {
|
|
90
|
+
console.error(e.message);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
' -- "$file" "$query" 2>/dev/null
|
|
94
|
+
}
|
|
95
|
+
|
|
49
96
|
# Parse options
|
|
50
97
|
INCLUDE_DECISIONS=false
|
|
51
98
|
INCLUDE_APP_MAP=false
|
|
52
99
|
INCLUDE_RULES=false
|
|
53
100
|
INCLUDE_LEARNINGS=false
|
|
54
101
|
INCLUDE_TEMPLATES=false
|
|
102
|
+
EXTRACT_PATTERNS=false
|
|
103
|
+
RESOLVE_CONFLICTS=false
|
|
104
|
+
ANALYSIS_MODE="balanced"
|
|
105
|
+
INCLUDE_EXAMPLES=false
|
|
55
106
|
|
|
56
107
|
while [ $# -gt 0 ]; do
|
|
57
108
|
case "$1" in
|
|
@@ -71,12 +122,26 @@ while [ $# -gt 0 ]; do
|
|
|
71
122
|
--templates)
|
|
72
123
|
INCLUDE_TEMPLATES=true
|
|
73
124
|
;;
|
|
125
|
+
--extract-patterns)
|
|
126
|
+
EXTRACT_PATTERNS=true
|
|
127
|
+
;;
|
|
128
|
+
--resolve-conflicts)
|
|
129
|
+
RESOLVE_CONFLICTS=true
|
|
130
|
+
;;
|
|
131
|
+
--analysis-mode)
|
|
132
|
+
shift
|
|
133
|
+
ANALYSIS_MODE="$1"
|
|
134
|
+
;;
|
|
135
|
+
--include-examples)
|
|
136
|
+
INCLUDE_EXAMPLES=true
|
|
137
|
+
;;
|
|
74
138
|
--full)
|
|
75
139
|
INCLUDE_DECISIONS=true
|
|
76
140
|
INCLUDE_APP_MAP=true
|
|
77
141
|
INCLUDE_RULES=true
|
|
78
142
|
INCLUDE_LEARNINGS=true
|
|
79
143
|
INCLUDE_TEMPLATES=true
|
|
144
|
+
EXTRACT_PATTERNS=true
|
|
80
145
|
;;
|
|
81
146
|
*)
|
|
82
147
|
echo -e "${RED}Unknown option: $1${NC}"
|
|
@@ -86,6 +151,9 @@ while [ $# -gt 0 ]; do
|
|
|
86
151
|
shift
|
|
87
152
|
done
|
|
88
153
|
|
|
154
|
+
# Determine scripts directory
|
|
155
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
156
|
+
|
|
89
157
|
# Create profiles directory
|
|
90
158
|
mkdir -p "$PROFILES_DIR"
|
|
91
159
|
|
|
@@ -278,6 +346,105 @@ ROADMAP_EOF
|
|
|
278
346
|
fi
|
|
279
347
|
fi
|
|
280
348
|
|
|
349
|
+
# ==========================================
|
|
350
|
+
# PATTERN EXTRACTION (NEW)
|
|
351
|
+
# ==========================================
|
|
352
|
+
if [ "$EXTRACT_PATTERNS" = true ]; then
|
|
353
|
+
echo ""
|
|
354
|
+
echo -e "${YELLOW}Pattern Extraction:${NC}"
|
|
355
|
+
|
|
356
|
+
mkdir -p "$PROFILE_DIR/.workflow/extracted"
|
|
357
|
+
|
|
358
|
+
# Build extractor options
|
|
359
|
+
EXTRACTOR_OPTS="--format json --with-conflicts --analysis-mode $ANALYSIS_MODE"
|
|
360
|
+
|
|
361
|
+
# Run pattern extraction
|
|
362
|
+
echo -e " ${DIM}Scanning codebase for patterns...${NC}"
|
|
363
|
+
PATTERNS_OUTPUT="$PROFILE_DIR/.workflow/extracted/patterns.json"
|
|
364
|
+
|
|
365
|
+
if node "$SCRIPT_DIR/flow-pattern-extractor.js" $EXTRACTOR_OPTS --output "$PATTERNS_OUTPUT" 2>/dev/null; then
|
|
366
|
+
echo -e " ${GREEN}✓${NC} patterns.json"
|
|
367
|
+
CONTENTS="$CONTENTS\n- .workflow/extracted/patterns.json (extracted patterns)"
|
|
368
|
+
|
|
369
|
+
# Check for conflicts (using safe JSON parsing instead of require())
|
|
370
|
+
CONFLICT_COUNT=$(safe_read_json "$PATTERNS_OUTPUT" "conflicts.length" || echo "0")
|
|
371
|
+
|
|
372
|
+
if [ "$CONFLICT_COUNT" -gt 0 ]; then
|
|
373
|
+
echo -e " ${YELLOW}!${NC} Found $CONFLICT_COUNT conflicts"
|
|
374
|
+
|
|
375
|
+
# Run conflict resolution if requested
|
|
376
|
+
if [ "$RESOLVE_CONFLICTS" = true ]; then
|
|
377
|
+
# Extract conflicts to temp file (using safe JSON parsing)
|
|
378
|
+
CONFLICTS_TEMP=$(mktemp)
|
|
379
|
+
safe_read_json "$PATTERNS_OUTPUT" "conflicts.json" > "$CONFLICTS_TEMP"
|
|
380
|
+
|
|
381
|
+
echo -e " ${DIM}Launching conflict resolver...${NC}"
|
|
382
|
+
|
|
383
|
+
RESOLVED_OUTPUT="$PROFILE_DIR/.workflow/extracted/conflicts-resolved.json"
|
|
384
|
+
if node "$SCRIPT_DIR/flow-conflict-resolver.js" --input "$CONFLICTS_TEMP" --output "$RESOLVED_OUTPUT"; then
|
|
385
|
+
echo -e " ${GREEN}✓${NC} conflicts-resolved.json"
|
|
386
|
+
CONTENTS="$CONTENTS\n- .workflow/extracted/conflicts-resolved.json (user resolutions)"
|
|
387
|
+
else
|
|
388
|
+
echo -e " ${YELLOW}!${NC} Conflict resolution skipped or cancelled"
|
|
389
|
+
fi
|
|
390
|
+
|
|
391
|
+
rm -f "$CONFLICTS_TEMP"
|
|
392
|
+
fi
|
|
393
|
+
else
|
|
394
|
+
echo -e " ${GREEN}✓${NC} No conflicting patterns found"
|
|
395
|
+
fi
|
|
396
|
+
|
|
397
|
+
# Extract examples if requested
|
|
398
|
+
if [ "$INCLUDE_EXAMPLES" = true ]; then
|
|
399
|
+
mkdir -p "$PROFILE_DIR/.workflow/extracted/examples"
|
|
400
|
+
|
|
401
|
+
# Extract example snippets from patterns (using safe JSON parsing)
|
|
402
|
+
node -e "
|
|
403
|
+
const fs = require('fs');
|
|
404
|
+
const path = require('path');
|
|
405
|
+
|
|
406
|
+
try {
|
|
407
|
+
const content = fs.readFileSync('$PATTERNS_OUTPUT', 'utf-8');
|
|
408
|
+
// Check for prototype pollution attempts
|
|
409
|
+
if (/__proto__|constructor\\s*[\"'\`:]|prototype\\s*[\"'\`:]/i.test(content)) {
|
|
410
|
+
console.error('Suspicious content detected');
|
|
411
|
+
process.exit(1);
|
|
412
|
+
}
|
|
413
|
+
const p = JSON.parse(content);
|
|
414
|
+
|
|
415
|
+
let count = 0;
|
|
416
|
+
for (const category in p.patterns) {
|
|
417
|
+
for (const pattern of p.patterns[category] || []) {
|
|
418
|
+
if (pattern.examples && pattern.examples.length > 0) {
|
|
419
|
+
const filename = pattern.subcategory.replace(/\\./g, '-') + '.txt';
|
|
420
|
+
const fileContent = pattern.name + '\\n' + '='.repeat(40) + '\\n\\n' +
|
|
421
|
+
pattern.examples.slice(0, 5).join('\\n');
|
|
422
|
+
fs.writeFileSync(
|
|
423
|
+
path.join('$PROFILE_DIR/.workflow/extracted/examples', filename),
|
|
424
|
+
fileContent
|
|
425
|
+
);
|
|
426
|
+
count++;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
console.log(count);
|
|
431
|
+
} catch(e) {
|
|
432
|
+
console.error(e.message);
|
|
433
|
+
process.exit(1);
|
|
434
|
+
}
|
|
435
|
+
" 2>/dev/null || true
|
|
436
|
+
|
|
437
|
+
EXAMPLE_COUNT=$(ls "$PROFILE_DIR/.workflow/extracted/examples/"*.txt 2>/dev/null | wc -l | tr -d ' ')
|
|
438
|
+
if [ "$EXAMPLE_COUNT" -gt 0 ]; then
|
|
439
|
+
echo -e " ${GREEN}✓${NC} examples/ ($EXAMPLE_COUNT pattern examples)"
|
|
440
|
+
CONTENTS="$CONTENTS\n- .workflow/extracted/examples/ (code snippets)"
|
|
441
|
+
fi
|
|
442
|
+
fi
|
|
443
|
+
else
|
|
444
|
+
echo -e " ${RED}✗${NC} Pattern extraction failed (continuing without patterns)"
|
|
445
|
+
fi
|
|
446
|
+
fi
|
|
447
|
+
|
|
281
448
|
# ==========================================
|
|
282
449
|
# Create profile info file
|
|
283
450
|
# ==========================================
|