wogiflow 1.0.0
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/agents/reviewer.md +81 -0
- package/.workflow/agents/security.md +94 -0
- package/.workflow/agents/story-writer.md +58 -0
- package/.workflow/bridges/base-bridge.js +395 -0
- package/.workflow/bridges/claude-bridge.js +434 -0
- package/.workflow/bridges/index.js +130 -0
- package/.workflow/lib/assumption-detector.js +481 -0
- package/.workflow/lib/config-substitution.js +371 -0
- package/.workflow/lib/failure-categories.js +478 -0
- package/.workflow/state/app-map.md.template +15 -0
- package/.workflow/state/architecture.md.template +24 -0
- package/.workflow/state/component-index.json.template +5 -0
- package/.workflow/state/decisions.md.template +15 -0
- package/.workflow/state/feedback-patterns.md.template +9 -0
- package/.workflow/state/knowledge-sync.json.template +6 -0
- package/.workflow/state/progress.md.template +14 -0
- package/.workflow/state/ready.json.template +7 -0
- package/.workflow/state/request-log.md.template +14 -0
- package/.workflow/state/session-state.json.template +11 -0
- package/.workflow/state/stack.md.template +33 -0
- package/.workflow/state/testing.md.template +36 -0
- package/.workflow/templates/claude-md.hbs +257 -0
- package/.workflow/templates/correction-report.md +67 -0
- package/.workflow/templates/gemini-md.hbs +52 -0
- package/README.md +1802 -0
- package/bin/flow +205 -0
- package/lib/index.js +33 -0
- package/lib/installer.js +467 -0
- package/lib/release-channel.js +269 -0
- package/lib/skill-registry.js +526 -0
- package/lib/upgrader.js +401 -0
- package/lib/utils.js +305 -0
- package/package.json +64 -0
- package/scripts/flow +985 -0
- package/scripts/flow-adaptive-learning.js +1259 -0
- package/scripts/flow-aggregate.js +488 -0
- package/scripts/flow-archive +133 -0
- package/scripts/flow-auto-context.js +1015 -0
- package/scripts/flow-auto-learn.js +615 -0
- package/scripts/flow-bridge.js +223 -0
- package/scripts/flow-browser-suggest.js +316 -0
- package/scripts/flow-bug.js +247 -0
- package/scripts/flow-cascade.js +711 -0
- package/scripts/flow-changelog +85 -0
- package/scripts/flow-checkpoint.js +483 -0
- package/scripts/flow-cli.js +403 -0
- package/scripts/flow-code-intelligence.js +760 -0
- package/scripts/flow-complexity.js +502 -0
- package/scripts/flow-config-set.js +152 -0
- package/scripts/flow-constants.js +157 -0
- package/scripts/flow-context +152 -0
- package/scripts/flow-context-init.js +482 -0
- package/scripts/flow-context-monitor.js +384 -0
- package/scripts/flow-context-scoring.js +886 -0
- package/scripts/flow-correct.js +458 -0
- package/scripts/flow-damage-control.js +985 -0
- package/scripts/flow-deps +101 -0
- package/scripts/flow-diff.js +700 -0
- package/scripts/flow-done +151 -0
- package/scripts/flow-done.js +489 -0
- package/scripts/flow-durable-session.js +1541 -0
- package/scripts/flow-entropy-monitor.js +345 -0
- package/scripts/flow-export-profile +349 -0
- package/scripts/flow-export-scanner.js +1046 -0
- package/scripts/flow-figma-confirm.js +400 -0
- package/scripts/flow-figma-extract.js +496 -0
- package/scripts/flow-figma-generate.js +683 -0
- package/scripts/flow-figma-index.js +909 -0
- package/scripts/flow-figma-match.js +617 -0
- package/scripts/flow-figma-mcp-server.js +518 -0
- package/scripts/flow-figma-pipeline.js +414 -0
- package/scripts/flow-file-ops.js +301 -0
- package/scripts/flow-gate-confidence.js +825 -0
- package/scripts/flow-guided-edit.js +659 -0
- package/scripts/flow-health +185 -0
- package/scripts/flow-health.js +413 -0
- package/scripts/flow-hooks.js +556 -0
- package/scripts/flow-http-client.js +249 -0
- package/scripts/flow-hybrid-detect.js +167 -0
- package/scripts/flow-hybrid-interactive.js +591 -0
- package/scripts/flow-hybrid-test.js +152 -0
- package/scripts/flow-import-profile +439 -0
- package/scripts/flow-init +253 -0
- package/scripts/flow-instruction-richness.js +827 -0
- package/scripts/flow-jira-integration.js +579 -0
- package/scripts/flow-knowledge-router.js +522 -0
- package/scripts/flow-knowledge-sync.js +589 -0
- package/scripts/flow-linear-integration.js +631 -0
- package/scripts/flow-links.js +774 -0
- package/scripts/flow-log-manager.js +559 -0
- package/scripts/flow-loop-enforcer.js +1246 -0
- package/scripts/flow-loop-retry-learning.js +630 -0
- package/scripts/flow-lsp.js +923 -0
- package/scripts/flow-map-index +348 -0
- package/scripts/flow-map-sync +201 -0
- package/scripts/flow-memory-blocks.js +668 -0
- package/scripts/flow-memory-compactor.js +350 -0
- package/scripts/flow-memory-db.js +1110 -0
- package/scripts/flow-memory-sync.js +484 -0
- package/scripts/flow-metrics.js +353 -0
- package/scripts/flow-migrate-ids.js +370 -0
- package/scripts/flow-model-adapter.js +802 -0
- package/scripts/flow-model-router.js +884 -0
- package/scripts/flow-models.js +1231 -0
- package/scripts/flow-morning.js +517 -0
- package/scripts/flow-multi-approach.js +660 -0
- package/scripts/flow-new-feature +86 -0
- package/scripts/flow-onboard +1042 -0
- package/scripts/flow-orchestrate-llm.js +459 -0
- package/scripts/flow-orchestrate.js +3592 -0
- package/scripts/flow-output.js +123 -0
- package/scripts/flow-parallel-detector.js +399 -0
- package/scripts/flow-parallel-dispatch.js +987 -0
- package/scripts/flow-parallel.js +428 -0
- package/scripts/flow-pattern-enforcer.js +600 -0
- package/scripts/flow-prd-manager.js +282 -0
- package/scripts/flow-progress.js +323 -0
- package/scripts/flow-project-analyzer.js +975 -0
- package/scripts/flow-prompt-composer.js +487 -0
- package/scripts/flow-providers.js +1381 -0
- package/scripts/flow-queue.js +308 -0
- package/scripts/flow-ready +82 -0
- package/scripts/flow-ready.js +189 -0
- package/scripts/flow-regression.js +396 -0
- package/scripts/flow-response-parser.js +450 -0
- package/scripts/flow-resume.js +284 -0
- package/scripts/flow-rules-sync.js +439 -0
- package/scripts/flow-run-trace.js +718 -0
- package/scripts/flow-safety.js +587 -0
- package/scripts/flow-search +104 -0
- package/scripts/flow-security.js +481 -0
- package/scripts/flow-session-end +106 -0
- package/scripts/flow-session-end.js +437 -0
- package/scripts/flow-session-state.js +671 -0
- package/scripts/flow-setup-hooks +216 -0
- package/scripts/flow-setup-hooks.js +377 -0
- package/scripts/flow-skill-create.js +329 -0
- package/scripts/flow-skill-creator.js +572 -0
- package/scripts/flow-skill-generator.js +1046 -0
- package/scripts/flow-skill-learn.js +880 -0
- package/scripts/flow-skill-matcher.js +578 -0
- package/scripts/flow-spec-generator.js +820 -0
- package/scripts/flow-stack-wizard.js +895 -0
- package/scripts/flow-standup +162 -0
- package/scripts/flow-start +74 -0
- package/scripts/flow-start.js +235 -0
- package/scripts/flow-status +110 -0
- package/scripts/flow-status.js +301 -0
- package/scripts/flow-step-browser.js +83 -0
- package/scripts/flow-step-changelog.js +217 -0
- package/scripts/flow-step-comments.js +306 -0
- package/scripts/flow-step-complexity.js +234 -0
- package/scripts/flow-step-coverage.js +218 -0
- package/scripts/flow-step-knowledge.js +193 -0
- package/scripts/flow-step-pr-tests.js +364 -0
- package/scripts/flow-step-regression.js +89 -0
- package/scripts/flow-step-review.js +516 -0
- package/scripts/flow-step-security.js +162 -0
- package/scripts/flow-step-silent-failures.js +290 -0
- package/scripts/flow-step-simplifier.js +346 -0
- package/scripts/flow-story +105 -0
- package/scripts/flow-story.js +500 -0
- package/scripts/flow-suspend.js +252 -0
- package/scripts/flow-sync-daemon.js +654 -0
- package/scripts/flow-task-analyzer.js +606 -0
- package/scripts/flow-team-dashboard.js +748 -0
- package/scripts/flow-team-sync.js +752 -0
- package/scripts/flow-team.js +977 -0
- package/scripts/flow-tech-options.js +528 -0
- package/scripts/flow-templates.js +812 -0
- package/scripts/flow-tiered-learning.js +728 -0
- package/scripts/flow-trace +204 -0
- package/scripts/flow-transcript-chunking.js +1106 -0
- package/scripts/flow-transcript-digest.js +7918 -0
- package/scripts/flow-transcript-language.js +465 -0
- package/scripts/flow-transcript-parsing.js +1085 -0
- package/scripts/flow-transcript-stories.js +2194 -0
- package/scripts/flow-update-map +224 -0
- package/scripts/flow-utils.js +2242 -0
- package/scripts/flow-verification.js +644 -0
- package/scripts/flow-verify.js +1177 -0
- package/scripts/flow-voice-input.js +638 -0
- package/scripts/flow-watch +168 -0
- package/scripts/flow-workflow-steps.js +521 -0
- package/scripts/flow-workflow.js +1029 -0
- package/scripts/flow-worktree.js +489 -0
- package/scripts/hooks/adapters/base-adapter.js +102 -0
- package/scripts/hooks/adapters/claude-code.js +359 -0
- package/scripts/hooks/adapters/index.js +79 -0
- package/scripts/hooks/core/component-check.js +341 -0
- package/scripts/hooks/core/index.js +35 -0
- package/scripts/hooks/core/loop-check.js +241 -0
- package/scripts/hooks/core/session-context.js +294 -0
- package/scripts/hooks/core/task-gate.js +177 -0
- package/scripts/hooks/core/validation.js +230 -0
- package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
- package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
- package/scripts/hooks/entry/claude-code/session-end.js +87 -0
- package/scripts/hooks/entry/claude-code/session-start.js +46 -0
- package/scripts/hooks/entry/claude-code/stop.js +43 -0
- package/scripts/postinstall.js +139 -0
- package/templates/browser-test-flow.json +56 -0
- package/templates/bug-report.md +43 -0
- package/templates/component-detail.md +42 -0
- package/templates/component.stories.tsx +49 -0
- package/templates/context/constraints.md +83 -0
- package/templates/context/conventions.md +177 -0
- package/templates/context/stack.md +60 -0
- package/templates/correction-report.md +90 -0
- package/templates/feature-proposal.md +35 -0
- package/templates/hybrid/_base.md +254 -0
- package/templates/hybrid/_patterns.md +45 -0
- package/templates/hybrid/create-component.md +127 -0
- package/templates/hybrid/create-file.md +56 -0
- package/templates/hybrid/create-hook.md +145 -0
- package/templates/hybrid/create-service.md +70 -0
- package/templates/hybrid/fix-bug.md +33 -0
- package/templates/hybrid/modify-file.md +55 -0
- package/templates/story.md +68 -0
- package/templates/task.json +56 -0
- package/templates/trace.md +69 -0
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Guided Edit Mode
|
|
5
|
+
*
|
|
6
|
+
* Step-by-step guided editing for multi-file changes.
|
|
7
|
+
* Inspired by Augment Code's "Next Edit" feature.
|
|
8
|
+
*
|
|
9
|
+
* Use cases:
|
|
10
|
+
* - Large refactors (rename component across 20 files)
|
|
11
|
+
* - Library upgrades (update imports everywhere)
|
|
12
|
+
* - Schema changes (add field to entity + DTOs + validators)
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* node scripts/flow-guided-edit.js start "rename Button to BaseButton"
|
|
16
|
+
* node scripts/flow-guided-edit.js next # Show next file
|
|
17
|
+
* node scripts/flow-guided-edit.js approve # Approve current
|
|
18
|
+
* node scripts/flow-guided-edit.js reject # Reject current
|
|
19
|
+
* node scripts/flow-guided-edit.js status # Show progress
|
|
20
|
+
* node scripts/flow-guided-edit.js abort # Cancel session
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
const { execSync } = require('child_process');
|
|
26
|
+
const {
|
|
27
|
+
getProjectRoot,
|
|
28
|
+
getConfig,
|
|
29
|
+
PATHS,
|
|
30
|
+
color,
|
|
31
|
+
success,
|
|
32
|
+
warn,
|
|
33
|
+
error,
|
|
34
|
+
readFile,
|
|
35
|
+
writeFile,
|
|
36
|
+
writeJson
|
|
37
|
+
} = require('./flow-utils');
|
|
38
|
+
|
|
39
|
+
const PROJECT_ROOT = getProjectRoot();
|
|
40
|
+
const SESSION_FILE = path.join(PATHS.state, 'guided-edit-session.json');
|
|
41
|
+
|
|
42
|
+
// ============================================================
|
|
43
|
+
// Session Management
|
|
44
|
+
// ============================================================
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Load current guided edit session
|
|
48
|
+
*/
|
|
49
|
+
function loadSession() {
|
|
50
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Save guided edit session
|
|
62
|
+
*/
|
|
63
|
+
function saveSession(session) {
|
|
64
|
+
writeJson(SESSION_FILE, session);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Clear guided edit session
|
|
69
|
+
*/
|
|
70
|
+
function clearSession() {
|
|
71
|
+
if (fs.existsSync(SESSION_FILE)) {
|
|
72
|
+
fs.unlinkSync(SESSION_FILE);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ============================================================
|
|
77
|
+
// File Analysis
|
|
78
|
+
// ============================================================
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Extract search pattern from description
|
|
82
|
+
* Returns { search: string, replace: string, type: 'rename'|'find'|'pattern' }
|
|
83
|
+
*/
|
|
84
|
+
function parseDescription(description) {
|
|
85
|
+
const desc = description.trim();
|
|
86
|
+
|
|
87
|
+
// Pattern: "rename X to Y"
|
|
88
|
+
const renameMatch = desc.match(/rename\s+['"]?(\w+)['"]?\s+to\s+['"]?(\w+)['"]?/i);
|
|
89
|
+
if (renameMatch) {
|
|
90
|
+
return {
|
|
91
|
+
type: 'rename',
|
|
92
|
+
search: renameMatch[1],
|
|
93
|
+
replace: renameMatch[2],
|
|
94
|
+
description: desc
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Pattern: "replace X with Y"
|
|
99
|
+
const replaceMatch = desc.match(/replace\s+['"]?([^'"]+)['"]?\s+with\s+['"]?([^'"]+)['"]?/i);
|
|
100
|
+
if (replaceMatch) {
|
|
101
|
+
return {
|
|
102
|
+
type: 'replace',
|
|
103
|
+
search: replaceMatch[1],
|
|
104
|
+
replace: replaceMatch[2],
|
|
105
|
+
description: desc
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Pattern: "find X" - just search
|
|
110
|
+
const findMatch = desc.match(/find\s+['"]?([^'"]+)['"]?/i);
|
|
111
|
+
if (findMatch) {
|
|
112
|
+
return {
|
|
113
|
+
type: 'find',
|
|
114
|
+
search: findMatch[1],
|
|
115
|
+
replace: null,
|
|
116
|
+
description: desc
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Pattern: "update X" - just search for X
|
|
121
|
+
const updateMatch = desc.match(/update\s+['"]?(\w+)['"]?/i);
|
|
122
|
+
if (updateMatch) {
|
|
123
|
+
return {
|
|
124
|
+
type: 'update',
|
|
125
|
+
search: updateMatch[1],
|
|
126
|
+
replace: null,
|
|
127
|
+
description: desc
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Default: treat entire description as search term
|
|
132
|
+
return {
|
|
133
|
+
type: 'search',
|
|
134
|
+
search: desc,
|
|
135
|
+
replace: null,
|
|
136
|
+
description: desc
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Find all files containing the search pattern
|
|
142
|
+
*/
|
|
143
|
+
function findAffectedFiles(search, options = {}) {
|
|
144
|
+
const config = getConfig();
|
|
145
|
+
|
|
146
|
+
// Determine source directory: options > config > src > project root
|
|
147
|
+
// But verify the directory exists before using it
|
|
148
|
+
let srcDir = null;
|
|
149
|
+
|
|
150
|
+
// Try options first
|
|
151
|
+
if (options.srcDir) {
|
|
152
|
+
srcDir = path.isAbsolute(options.srcDir) ? options.srcDir : path.join(PROJECT_ROOT, options.srcDir);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Try config
|
|
156
|
+
if (!srcDir || !fs.existsSync(srcDir)) {
|
|
157
|
+
const configSrcDir = config.guidedEdit?.srcDir;
|
|
158
|
+
if (configSrcDir) {
|
|
159
|
+
const resolved = path.isAbsolute(configSrcDir) ? configSrcDir : path.join(PROJECT_ROOT, configSrcDir);
|
|
160
|
+
if (fs.existsSync(resolved)) {
|
|
161
|
+
srcDir = resolved;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Fallback to src/ or project root
|
|
167
|
+
if (!srcDir || !fs.existsSync(srcDir)) {
|
|
168
|
+
const defaultSrc = path.join(PROJECT_ROOT, 'src');
|
|
169
|
+
srcDir = fs.existsSync(defaultSrc) ? defaultSrc : PROJECT_ROOT;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const extensions = options.extensions || config.guidedEdit?.extensions || ['ts', 'tsx', 'js', 'jsx', 'vue', 'svelte'];
|
|
173
|
+
|
|
174
|
+
const results = [];
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const extPattern = extensions.map(e => `--include="*.${e}"`).join(' ');
|
|
178
|
+
const output = execSync(
|
|
179
|
+
`grep -rl "${search}" ${extPattern} "${srcDir}" 2>/dev/null`,
|
|
180
|
+
{ encoding: 'utf-8', timeout: 30000 }
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const files = output.split('\n').filter(f => f.trim());
|
|
184
|
+
|
|
185
|
+
for (const file of files) {
|
|
186
|
+
try {
|
|
187
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
188
|
+
const lines = content.split('\n');
|
|
189
|
+
const matches = [];
|
|
190
|
+
|
|
191
|
+
for (let i = 0; i < lines.length; i++) {
|
|
192
|
+
if (lines[i].includes(search)) {
|
|
193
|
+
matches.push({
|
|
194
|
+
line: i + 1,
|
|
195
|
+
content: lines[i].trim().substring(0, 100)
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
results.push({
|
|
201
|
+
path: path.relative(PROJECT_ROOT, file),
|
|
202
|
+
absolutePath: file,
|
|
203
|
+
matchCount: matches.length,
|
|
204
|
+
matches: matches.slice(0, 5), // First 5 matches
|
|
205
|
+
status: 'pending'
|
|
206
|
+
});
|
|
207
|
+
} catch {
|
|
208
|
+
// Skip files that can't be read
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} catch {
|
|
212
|
+
// No matches or grep failed
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Sort by match count (more matches first)
|
|
216
|
+
results.sort((a, b) => b.matchCount - a.matchCount);
|
|
217
|
+
|
|
218
|
+
return results;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Generate a preview of changes for a file
|
|
223
|
+
*/
|
|
224
|
+
function generatePreview(file, search, replace) {
|
|
225
|
+
if (!replace) {
|
|
226
|
+
return { before: null, after: null, diff: null };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
const content = fs.readFileSync(file.absolutePath, 'utf-8');
|
|
231
|
+
const newContent = content.replace(new RegExp(search, 'g'), replace);
|
|
232
|
+
|
|
233
|
+
if (content === newContent) {
|
|
234
|
+
return { before: content, after: content, diff: null, unchanged: true };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Generate simple diff
|
|
238
|
+
const oldLines = content.split('\n');
|
|
239
|
+
const newLines = newContent.split('\n');
|
|
240
|
+
const diff = [];
|
|
241
|
+
|
|
242
|
+
for (let i = 0; i < Math.max(oldLines.length, newLines.length); i++) {
|
|
243
|
+
if (oldLines[i] !== newLines[i]) {
|
|
244
|
+
if (oldLines[i]) diff.push(`- ${oldLines[i]}`);
|
|
245
|
+
if (newLines[i]) diff.push(`+ ${newLines[i]}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
before: content,
|
|
251
|
+
after: newContent,
|
|
252
|
+
diff: diff.slice(0, 20).join('\n') + (diff.length > 20 ? '\n...' : '')
|
|
253
|
+
};
|
|
254
|
+
} catch (err) {
|
|
255
|
+
return { error: err.message };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// ============================================================
|
|
260
|
+
// Session Operations
|
|
261
|
+
// ============================================================
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Start a new guided edit session
|
|
265
|
+
*/
|
|
266
|
+
function startSession(description, options = {}) {
|
|
267
|
+
const existing = loadSession();
|
|
268
|
+
if (existing) {
|
|
269
|
+
error('A guided edit session is already in progress');
|
|
270
|
+
console.log(color('dim', `Description: "${existing.description}"`));
|
|
271
|
+
console.log(color('dim', `Progress: ${existing.files.filter(f => f.status !== 'pending').length}/${existing.files.length}`));
|
|
272
|
+
console.log('');
|
|
273
|
+
console.log('Run: node scripts/flow-guided-edit.js abort to cancel');
|
|
274
|
+
console.log(' node scripts/flow-guided-edit.js status to see progress');
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const parsed = parseDescription(description);
|
|
279
|
+
console.log(color('cyan', '🔍 Analyzing change...'));
|
|
280
|
+
console.log(` Type: ${parsed.type}`);
|
|
281
|
+
console.log(` Search: "${parsed.search}"`);
|
|
282
|
+
if (parsed.replace) {
|
|
283
|
+
console.log(` Replace: "${parsed.replace}"`);
|
|
284
|
+
}
|
|
285
|
+
console.log('');
|
|
286
|
+
|
|
287
|
+
const files = findAffectedFiles(parsed.search, options);
|
|
288
|
+
|
|
289
|
+
if (files.length === 0) {
|
|
290
|
+
warn(`No files found containing "${parsed.search}"`);
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
console.log(color('green', `Found ${files.length} file(s) to review:`));
|
|
295
|
+
for (const file of files.slice(0, 10)) {
|
|
296
|
+
console.log(` ${file.path} (${file.matchCount} match${file.matchCount > 1 ? 'es' : ''})`);
|
|
297
|
+
}
|
|
298
|
+
if (files.length > 10) {
|
|
299
|
+
console.log(color('dim', ` ... and ${files.length - 10} more`));
|
|
300
|
+
}
|
|
301
|
+
console.log('');
|
|
302
|
+
|
|
303
|
+
const session = {
|
|
304
|
+
id: `ge-${Date.now()}`,
|
|
305
|
+
description: parsed.description,
|
|
306
|
+
type: parsed.type,
|
|
307
|
+
search: parsed.search,
|
|
308
|
+
replace: parsed.replace,
|
|
309
|
+
files,
|
|
310
|
+
currentIndex: 0,
|
|
311
|
+
startedAt: new Date().toISOString(),
|
|
312
|
+
stats: {
|
|
313
|
+
approved: 0,
|
|
314
|
+
rejected: 0,
|
|
315
|
+
skipped: 0
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
saveSession(session);
|
|
320
|
+
success('Guided edit session started');
|
|
321
|
+
console.log('');
|
|
322
|
+
console.log('Commands:');
|
|
323
|
+
console.log(` ${color('cyan', 'next')} - Show next file to review`);
|
|
324
|
+
console.log(` ${color('green', 'approve')} - Approve and apply change`);
|
|
325
|
+
console.log(` ${color('yellow', 'reject')} - Reject and skip file`);
|
|
326
|
+
console.log(` ${color('dim', 'status')} - Show progress`);
|
|
327
|
+
console.log(` ${color('red', 'abort')} - Cancel session`);
|
|
328
|
+
|
|
329
|
+
return session;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Show the next file to review
|
|
334
|
+
*/
|
|
335
|
+
function showNext() {
|
|
336
|
+
const session = loadSession();
|
|
337
|
+
if (!session) {
|
|
338
|
+
error('No guided edit session in progress');
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Find next pending file
|
|
343
|
+
const pending = session.files.filter(f => f.status === 'pending');
|
|
344
|
+
if (pending.length === 0) {
|
|
345
|
+
success('All files reviewed!');
|
|
346
|
+
showSummary(session);
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const file = pending[0];
|
|
351
|
+
const preview = generatePreview(file, session.search, session.replace);
|
|
352
|
+
|
|
353
|
+
console.log(color('cyan', '─'.repeat(60)));
|
|
354
|
+
console.log(color('cyan', `📄 File ${session.files.length - pending.length + 1}/${session.files.length}`));
|
|
355
|
+
console.log(color('cyan', '─'.repeat(60)));
|
|
356
|
+
console.log(`Path: ${file.path}`);
|
|
357
|
+
console.log(`Matches: ${file.matchCount}`);
|
|
358
|
+
console.log('');
|
|
359
|
+
|
|
360
|
+
if (file.matches && file.matches.length > 0) {
|
|
361
|
+
console.log(color('dim', 'Match locations:'));
|
|
362
|
+
for (const match of file.matches) {
|
|
363
|
+
console.log(color('dim', ` Line ${match.line}: ${match.content}`));
|
|
364
|
+
}
|
|
365
|
+
console.log('');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (preview.diff) {
|
|
369
|
+
console.log(color('yellow', 'Proposed changes:'));
|
|
370
|
+
console.log(preview.diff);
|
|
371
|
+
console.log('');
|
|
372
|
+
} else if (preview.unchanged) {
|
|
373
|
+
console.log(color('dim', '(No actual changes needed - pattern not found in replaceable context)'));
|
|
374
|
+
console.log('');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
console.log(color('cyan', '─'.repeat(60)));
|
|
378
|
+
console.log(`[${color('green', 'a')}]pprove [${color('yellow', 'r')}]eject [${color('dim', 's')}]kip [${color('red', 'q')}]uit`);
|
|
379
|
+
|
|
380
|
+
return { session, file, preview };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Approve the current file's changes
|
|
385
|
+
*/
|
|
386
|
+
function approveFile() {
|
|
387
|
+
const session = loadSession();
|
|
388
|
+
if (!session) {
|
|
389
|
+
error('No guided edit session in progress');
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const pending = session.files.filter(f => f.status === 'pending');
|
|
394
|
+
if (pending.length === 0) {
|
|
395
|
+
warn('No pending files to approve');
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const file = pending[0];
|
|
400
|
+
|
|
401
|
+
// Apply the change if we have a replacement
|
|
402
|
+
if (session.replace) {
|
|
403
|
+
try {
|
|
404
|
+
const content = fs.readFileSync(file.absolutePath, 'utf-8');
|
|
405
|
+
const newContent = content.replace(new RegExp(session.search, 'g'), session.replace);
|
|
406
|
+
fs.writeFileSync(file.absolutePath, newContent);
|
|
407
|
+
} catch (err) {
|
|
408
|
+
error(`Failed to apply changes: ${err.message}`);
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
file.status = 'approved';
|
|
414
|
+
session.stats.approved++;
|
|
415
|
+
saveSession(session);
|
|
416
|
+
|
|
417
|
+
success(`Approved: ${file.path}`);
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Reject the current file's changes
|
|
423
|
+
*/
|
|
424
|
+
function rejectFile() {
|
|
425
|
+
const session = loadSession();
|
|
426
|
+
if (!session) {
|
|
427
|
+
error('No guided edit session in progress');
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const pending = session.files.filter(f => f.status === 'pending');
|
|
432
|
+
if (pending.length === 0) {
|
|
433
|
+
warn('No pending files to reject');
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const file = pending[0];
|
|
438
|
+
file.status = 'rejected';
|
|
439
|
+
session.stats.rejected++;
|
|
440
|
+
saveSession(session);
|
|
441
|
+
|
|
442
|
+
warn(`Rejected: ${file.path}`);
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Skip the current file
|
|
448
|
+
*/
|
|
449
|
+
function skipFile() {
|
|
450
|
+
const session = loadSession();
|
|
451
|
+
if (!session) {
|
|
452
|
+
error('No guided edit session in progress');
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const pending = session.files.filter(f => f.status === 'pending');
|
|
457
|
+
if (pending.length === 0) {
|
|
458
|
+
warn('No pending files to skip');
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const file = pending[0];
|
|
463
|
+
file.status = 'skipped';
|
|
464
|
+
session.stats.skipped++;
|
|
465
|
+
saveSession(session);
|
|
466
|
+
|
|
467
|
+
console.log(color('dim', `Skipped: ${file.path}`));
|
|
468
|
+
return true;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Show session status
|
|
473
|
+
*/
|
|
474
|
+
function showStatus() {
|
|
475
|
+
const session = loadSession();
|
|
476
|
+
if (!session) {
|
|
477
|
+
console.log(color('dim', 'No guided edit session in progress'));
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const pending = session.files.filter(f => f.status === 'pending').length;
|
|
482
|
+
const reviewed = session.files.length - pending;
|
|
483
|
+
|
|
484
|
+
console.log(color('cyan', '─'.repeat(40)));
|
|
485
|
+
console.log(color('cyan', '📊 Guided Edit Status'));
|
|
486
|
+
console.log(color('cyan', '─'.repeat(40)));
|
|
487
|
+
console.log(`Description: "${session.description}"`);
|
|
488
|
+
console.log(`Type: ${session.type}`);
|
|
489
|
+
console.log(`Search: "${session.search}"`);
|
|
490
|
+
if (session.replace) {
|
|
491
|
+
console.log(`Replace: "${session.replace}"`);
|
|
492
|
+
}
|
|
493
|
+
console.log('');
|
|
494
|
+
console.log(`Progress: ${reviewed}/${session.files.length} files reviewed`);
|
|
495
|
+
console.log(` ${color('green', '✓')} Approved: ${session.stats.approved}`);
|
|
496
|
+
console.log(` ${color('yellow', '✗')} Rejected: ${session.stats.rejected}`);
|
|
497
|
+
console.log(` ${color('dim', '○')} Skipped: ${session.stats.skipped}`);
|
|
498
|
+
console.log(` ${color('cyan', '•')} Pending: ${pending}`);
|
|
499
|
+
console.log('');
|
|
500
|
+
|
|
501
|
+
return session;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Show summary after completion
|
|
506
|
+
*/
|
|
507
|
+
function showSummary(session) {
|
|
508
|
+
console.log('');
|
|
509
|
+
console.log(color('cyan', '═'.repeat(40)));
|
|
510
|
+
console.log(color('cyan', '📊 Session Complete'));
|
|
511
|
+
console.log(color('cyan', '═'.repeat(40)));
|
|
512
|
+
console.log(` ${color('green', '✓')} Approved: ${session.stats.approved}`);
|
|
513
|
+
console.log(` ${color('yellow', '✗')} Rejected: ${session.stats.rejected}`);
|
|
514
|
+
console.log(` ${color('dim', '○')} Skipped: ${session.stats.skipped}`);
|
|
515
|
+
console.log('');
|
|
516
|
+
|
|
517
|
+
if (session.stats.approved > 0) {
|
|
518
|
+
console.log(color('dim', 'Changes have been applied. Review and commit when ready.'));
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Clear session
|
|
522
|
+
clearSession();
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Abort the current session
|
|
527
|
+
*/
|
|
528
|
+
function abortSession() {
|
|
529
|
+
const session = loadSession();
|
|
530
|
+
if (!session) {
|
|
531
|
+
console.log(color('dim', 'No session to abort'));
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
showStatus();
|
|
536
|
+
clearSession();
|
|
537
|
+
warn('Session aborted');
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// ============================================================
|
|
541
|
+
// CLI
|
|
542
|
+
// ============================================================
|
|
543
|
+
|
|
544
|
+
function showHelp() {
|
|
545
|
+
console.log(`
|
|
546
|
+
Wogi Flow - Guided Edit Mode
|
|
547
|
+
|
|
548
|
+
Step-by-step guided editing for multi-file changes.
|
|
549
|
+
|
|
550
|
+
Usage:
|
|
551
|
+
flow guided-edit start "description" Start a new guided edit session
|
|
552
|
+
flow guided-edit next Show next file to review
|
|
553
|
+
flow guided-edit approve Approve and apply current file's changes
|
|
554
|
+
flow guided-edit reject Reject current file's changes
|
|
555
|
+
flow guided-edit skip Skip current file
|
|
556
|
+
flow guided-edit status Show progress
|
|
557
|
+
flow guided-edit abort Cancel session
|
|
558
|
+
|
|
559
|
+
Examples:
|
|
560
|
+
flow guided-edit start "rename Button to BaseButton"
|
|
561
|
+
flow guided-edit start "replace console.log with logger.debug"
|
|
562
|
+
flow guided-edit start "find deprecated API calls"
|
|
563
|
+
|
|
564
|
+
The session tracks progress across files. Use 'approve' to apply changes,
|
|
565
|
+
'reject' to skip without changes, or 'skip' to review later.
|
|
566
|
+
`);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function main() {
|
|
570
|
+
const args = process.argv.slice(2);
|
|
571
|
+
const command = args[0];
|
|
572
|
+
|
|
573
|
+
if (!command || command === '--help' || command === '-h') {
|
|
574
|
+
showHelp();
|
|
575
|
+
process.exit(0);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
switch (command) {
|
|
579
|
+
case 'start': {
|
|
580
|
+
const description = args.slice(1).join(' ');
|
|
581
|
+
if (!description) {
|
|
582
|
+
error('Please provide a description');
|
|
583
|
+
console.log('Example: flow guided-edit start "rename Button to BaseButton"');
|
|
584
|
+
process.exit(1);
|
|
585
|
+
}
|
|
586
|
+
startSession(description);
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
case 'next':
|
|
591
|
+
case 'n':
|
|
592
|
+
showNext();
|
|
593
|
+
break;
|
|
594
|
+
|
|
595
|
+
case 'approve':
|
|
596
|
+
case 'a':
|
|
597
|
+
case 'yes':
|
|
598
|
+
case 'y':
|
|
599
|
+
if (approveFile()) {
|
|
600
|
+
showNext();
|
|
601
|
+
}
|
|
602
|
+
break;
|
|
603
|
+
|
|
604
|
+
case 'reject':
|
|
605
|
+
case 'r':
|
|
606
|
+
case 'no':
|
|
607
|
+
if (rejectFile()) {
|
|
608
|
+
showNext();
|
|
609
|
+
}
|
|
610
|
+
break;
|
|
611
|
+
|
|
612
|
+
case 'skip':
|
|
613
|
+
case 's':
|
|
614
|
+
if (skipFile()) {
|
|
615
|
+
showNext();
|
|
616
|
+
}
|
|
617
|
+
break;
|
|
618
|
+
|
|
619
|
+
case 'status':
|
|
620
|
+
showStatus();
|
|
621
|
+
break;
|
|
622
|
+
|
|
623
|
+
case 'abort':
|
|
624
|
+
case 'cancel':
|
|
625
|
+
case 'q':
|
|
626
|
+
abortSession();
|
|
627
|
+
break;
|
|
628
|
+
|
|
629
|
+
default:
|
|
630
|
+
error(`Unknown command: ${command}`);
|
|
631
|
+
showHelp();
|
|
632
|
+
process.exit(1);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// ============================================================
|
|
637
|
+
// Exports
|
|
638
|
+
// ============================================================
|
|
639
|
+
|
|
640
|
+
module.exports = {
|
|
641
|
+
loadSession,
|
|
642
|
+
saveSession,
|
|
643
|
+
clearSession,
|
|
644
|
+
parseDescription,
|
|
645
|
+
findAffectedFiles,
|
|
646
|
+
generatePreview,
|
|
647
|
+
startSession,
|
|
648
|
+
showNext,
|
|
649
|
+
approveFile,
|
|
650
|
+
rejectFile,
|
|
651
|
+
skipFile,
|
|
652
|
+
showStatus,
|
|
653
|
+
abortSession,
|
|
654
|
+
SESSION_FILE
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
if (require.main === module) {
|
|
658
|
+
main();
|
|
659
|
+
}
|