wogiflow 1.0.18 → 1.0.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -11,7 +11,8 @@
11
11
  "flow": "./scripts/flow",
12
12
  "test": "node mcp-memory-server/test.js",
13
13
  "memory-server": "node mcp-memory-server/index.js",
14
- "postinstall": "node scripts/postinstall.js"
14
+ "postinstall": "node scripts/postinstall.js",
15
+ "preuninstall": "node scripts/preuninstall.js"
15
16
  },
16
17
  "files": [
17
18
  "bin/",
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * WogiFlow preuninstall script
5
+ *
6
+ * Runs before npm uninstall to clean up WogiFlow-created files.
7
+ *
8
+ * Removes:
9
+ * - .workflow/ directory (all WogiFlow state and config)
10
+ * - .claude/commands/wogi-*.md (WogiFlow slash commands)
11
+ * - .claude/docs/ (WogiFlow documentation)
12
+ * - .claude/skills/ (WogiFlow skills - may contain user customizations)
13
+ * - .claude/hooks/ (WogiFlow hooks)
14
+ * - .claude/rules/ (WogiFlow rules)
15
+ * - CLAUDE.md (if contains WogiFlow marker)
16
+ *
17
+ * Preserves:
18
+ * - .claude/ directory structure (user may have other content)
19
+ * - User's git history
20
+ */
21
+
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+
25
+ // Get project root (where npm uninstall is run)
26
+ const PROJECT_ROOT = process.env.INIT_CWD || process.cwd();
27
+
28
+ // Directories to remove completely
29
+ const DIRS_TO_REMOVE = [
30
+ path.join(PROJECT_ROOT, '.workflow'),
31
+ path.join(PROJECT_ROOT, '.claude', 'docs'),
32
+ path.join(PROJECT_ROOT, '.claude', 'skills'),
33
+ path.join(PROJECT_ROOT, '.claude', 'hooks'),
34
+ path.join(PROJECT_ROOT, '.claude', 'rules')
35
+ ];
36
+
37
+ // File patterns to remove
38
+ const CLAUDE_COMMANDS_DIR = path.join(PROJECT_ROOT, '.claude', 'commands');
39
+ const CLAUDE_MD_PATH = path.join(PROJECT_ROOT, 'CLAUDE.md');
40
+
41
+ // WogiFlow marker in CLAUDE.md - more explicit to avoid false positives
42
+ const WOGIFLOW_MARKER = 'WogiFlow methodology';
43
+
44
+ // Debug logging helper
45
+ function debugLog(message) {
46
+ if (process.env.DEBUG || process.env.WOGIFLOW_DEBUG) {
47
+ process.stderr.write(`[preuninstall] ${message}\n`);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Recursively remove a directory
53
+ */
54
+ function removeDir(dirPath) {
55
+ if (!fs.existsSync(dirPath)) {
56
+ return { removed: false, reason: 'not found' };
57
+ }
58
+
59
+ try {
60
+ fs.rmSync(dirPath, { recursive: true, force: true });
61
+ return { removed: true };
62
+ } catch (err) {
63
+ return { removed: false, reason: err.message };
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Remove WogiFlow command files (wogi-*.md)
69
+ */
70
+ function removeWogiCommands() {
71
+ if (!fs.existsSync(CLAUDE_COMMANDS_DIR)) {
72
+ return { count: 0, files: [] };
73
+ }
74
+
75
+ const removed = [];
76
+ const skipped = [];
77
+ try {
78
+ const files = fs.readdirSync(CLAUDE_COMMANDS_DIR);
79
+ for (const file of files) {
80
+ if (file.startsWith('wogi-') && file.endsWith('.md')) {
81
+ const filePath = path.join(CLAUDE_COMMANDS_DIR, file);
82
+ try {
83
+ fs.unlinkSync(filePath);
84
+ removed.push(file);
85
+ } catch (err) {
86
+ debugLog(`Failed to remove ${file}: ${err.message}`);
87
+ skipped.push(file);
88
+ }
89
+ }
90
+ }
91
+ } catch (err) {
92
+ debugLog(`Failed to read commands directory: ${err.message}`);
93
+ }
94
+
95
+ return { count: removed.length, files: removed, skipped };
96
+ }
97
+
98
+ /**
99
+ * Remove CLAUDE.md if it contains WogiFlow marker
100
+ * Note: Per security-patterns.md Rule #1, we don't use existsSync before readFileSync
101
+ * as it creates race conditions. The try-catch handles "file not found" gracefully.
102
+ */
103
+ function removeClaudeMd() {
104
+ try {
105
+ const content = fs.readFileSync(CLAUDE_MD_PATH, 'utf-8');
106
+ if (content.includes(WOGIFLOW_MARKER)) {
107
+ fs.unlinkSync(CLAUDE_MD_PATH);
108
+ return { removed: true };
109
+ }
110
+ return { removed: false, reason: 'not a WogiFlow file' };
111
+ } catch (err) {
112
+ if (err.code === 'ENOENT') {
113
+ return { removed: false, reason: 'not found' };
114
+ }
115
+ debugLog(`Failed to process CLAUDE.md: ${err.message}`);
116
+ return { removed: false, reason: err.message };
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Clean up empty .claude directory if nothing left
122
+ * Returns info about what was preserved (for user visibility)
123
+ */
124
+ function cleanupClaudeDir() {
125
+ const claudeDir = path.join(PROJECT_ROOT, '.claude');
126
+ const result = { removed: false, preserved: [] };
127
+
128
+ try {
129
+ const remaining = fs.readdirSync(claudeDir);
130
+
131
+ // Only remove if empty or only contains 'commands' with no files
132
+ if (remaining.length === 0) {
133
+ fs.rmdirSync(claudeDir);
134
+ result.removed = true;
135
+ } else if (remaining.length === 1 && remaining[0] === 'commands') {
136
+ const commandsDir = path.join(claudeDir, 'commands');
137
+ const commandFiles = fs.readdirSync(commandsDir);
138
+ if (commandFiles.length === 0) {
139
+ fs.rmdirSync(commandsDir);
140
+ fs.rmdirSync(claudeDir);
141
+ result.removed = true;
142
+ } else {
143
+ // Log non-WogiFlow files being preserved
144
+ result.preserved = commandFiles.filter(f => !f.startsWith('wogi-'));
145
+ if (result.preserved.length > 0) {
146
+ debugLog(`Preserving non-WogiFlow commands: ${result.preserved.join(', ')}`);
147
+ }
148
+ }
149
+ } else {
150
+ // Other content in .claude - log what's being preserved
151
+ result.preserved = remaining;
152
+ debugLog(`Preserving .claude contents: ${remaining.join(', ')}`);
153
+ }
154
+ } catch (err) {
155
+ if (err.code !== 'ENOENT') {
156
+ debugLog(`Cleanup error: ${err.message}`);
157
+ }
158
+ }
159
+
160
+ return result;
161
+ }
162
+
163
+ /**
164
+ * Check if we should be silent
165
+ */
166
+ function shouldBeSilent() {
167
+ return process.env.CI || process.env.WOGIFLOW_SILENT_UNINSTALL;
168
+ }
169
+
170
+ /**
171
+ * Main entry point
172
+ */
173
+ function main() {
174
+ const silent = shouldBeSilent();
175
+ const results = {
176
+ directories: [],
177
+ commands: null,
178
+ claudeMd: null
179
+ };
180
+
181
+ // Remove directories
182
+ for (const dir of DIRS_TO_REMOVE) {
183
+ const relativePath = path.relative(PROJECT_ROOT, dir);
184
+ const result = removeDir(dir);
185
+ results.directories.push({ path: relativePath, ...result });
186
+ }
187
+
188
+ // Remove wogi-*.md commands
189
+ results.commands = removeWogiCommands();
190
+
191
+ // Remove CLAUDE.md if WogiFlow-generated
192
+ results.claudeMd = removeClaudeMd();
193
+
194
+ // Clean up empty .claude directory
195
+ results.claudeDir = cleanupClaudeDir();
196
+
197
+ // Output summary
198
+ if (!silent) {
199
+ const removedDirs = results.directories.filter(d => d.removed);
200
+ const removedCount = removedDirs.length + results.commands.count + (results.claudeMd.removed ? 1 : 0);
201
+
202
+ if (removedCount > 0) {
203
+ process.stderr.write('\n\x1b[36mWogiFlow cleanup:\x1b[0m\n');
204
+
205
+ for (const dir of removedDirs) {
206
+ process.stderr.write(` \x1b[31m✗\x1b[0m Removed ${dir.path}/\n`);
207
+ }
208
+
209
+ if (results.commands.count > 0) {
210
+ process.stderr.write(` \x1b[31m✗\x1b[0m Removed ${results.commands.count} command(s): ${results.commands.files.join(', ')}\n`);
211
+ }
212
+
213
+ if (results.claudeMd.removed) {
214
+ process.stderr.write(` \x1b[31m✗\x1b[0m Removed CLAUDE.md\n`);
215
+ }
216
+
217
+ // Show preserved files (user's custom content)
218
+ if (results.claudeDir && results.claudeDir.preserved && results.claudeDir.preserved.length > 0) {
219
+ process.stderr.write(`\n\x1b[33mPreserved:\x1b[0m ${results.claudeDir.preserved.join(', ')} (not WogiFlow files)\n`);
220
+ }
221
+
222
+ process.stderr.write('\n\x1b[2mWogiFlow has been uninstalled. Your git history is preserved.\x1b[0m\n\n');
223
+ } else {
224
+ process.stderr.write('\x1b[36mWogiFlow:\x1b[0m No files to clean up.\n');
225
+ }
226
+ }
227
+ }
228
+
229
+ // Run
230
+ try {
231
+ main();
232
+ } catch (err) {
233
+ // Don't fail npm uninstall on preuninstall errors
234
+ if (!process.env.CI) {
235
+ process.stderr.write(`\x1b[33mWogiFlow cleanup warning:\x1b[0m ${err.message}\n`);
236
+ }
237
+ }