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.
Files changed (221) hide show
  1. package/.workflow/agents/reviewer.md +81 -0
  2. package/.workflow/agents/security.md +94 -0
  3. package/.workflow/agents/story-writer.md +58 -0
  4. package/.workflow/bridges/base-bridge.js +395 -0
  5. package/.workflow/bridges/claude-bridge.js +434 -0
  6. package/.workflow/bridges/index.js +130 -0
  7. package/.workflow/lib/assumption-detector.js +481 -0
  8. package/.workflow/lib/config-substitution.js +371 -0
  9. package/.workflow/lib/failure-categories.js +478 -0
  10. package/.workflow/state/app-map.md.template +15 -0
  11. package/.workflow/state/architecture.md.template +24 -0
  12. package/.workflow/state/component-index.json.template +5 -0
  13. package/.workflow/state/decisions.md.template +15 -0
  14. package/.workflow/state/feedback-patterns.md.template +9 -0
  15. package/.workflow/state/knowledge-sync.json.template +6 -0
  16. package/.workflow/state/progress.md.template +14 -0
  17. package/.workflow/state/ready.json.template +7 -0
  18. package/.workflow/state/request-log.md.template +14 -0
  19. package/.workflow/state/session-state.json.template +11 -0
  20. package/.workflow/state/stack.md.template +33 -0
  21. package/.workflow/state/testing.md.template +36 -0
  22. package/.workflow/templates/claude-md.hbs +257 -0
  23. package/.workflow/templates/correction-report.md +67 -0
  24. package/.workflow/templates/gemini-md.hbs +52 -0
  25. package/README.md +1802 -0
  26. package/bin/flow +205 -0
  27. package/lib/index.js +33 -0
  28. package/lib/installer.js +467 -0
  29. package/lib/release-channel.js +269 -0
  30. package/lib/skill-registry.js +526 -0
  31. package/lib/upgrader.js +401 -0
  32. package/lib/utils.js +305 -0
  33. package/package.json +64 -0
  34. package/scripts/flow +985 -0
  35. package/scripts/flow-adaptive-learning.js +1259 -0
  36. package/scripts/flow-aggregate.js +488 -0
  37. package/scripts/flow-archive +133 -0
  38. package/scripts/flow-auto-context.js +1015 -0
  39. package/scripts/flow-auto-learn.js +615 -0
  40. package/scripts/flow-bridge.js +223 -0
  41. package/scripts/flow-browser-suggest.js +316 -0
  42. package/scripts/flow-bug.js +247 -0
  43. package/scripts/flow-cascade.js +711 -0
  44. package/scripts/flow-changelog +85 -0
  45. package/scripts/flow-checkpoint.js +483 -0
  46. package/scripts/flow-cli.js +403 -0
  47. package/scripts/flow-code-intelligence.js +760 -0
  48. package/scripts/flow-complexity.js +502 -0
  49. package/scripts/flow-config-set.js +152 -0
  50. package/scripts/flow-constants.js +157 -0
  51. package/scripts/flow-context +152 -0
  52. package/scripts/flow-context-init.js +482 -0
  53. package/scripts/flow-context-monitor.js +384 -0
  54. package/scripts/flow-context-scoring.js +886 -0
  55. package/scripts/flow-correct.js +458 -0
  56. package/scripts/flow-damage-control.js +985 -0
  57. package/scripts/flow-deps +101 -0
  58. package/scripts/flow-diff.js +700 -0
  59. package/scripts/flow-done +151 -0
  60. package/scripts/flow-done.js +489 -0
  61. package/scripts/flow-durable-session.js +1541 -0
  62. package/scripts/flow-entropy-monitor.js +345 -0
  63. package/scripts/flow-export-profile +349 -0
  64. package/scripts/flow-export-scanner.js +1046 -0
  65. package/scripts/flow-figma-confirm.js +400 -0
  66. package/scripts/flow-figma-extract.js +496 -0
  67. package/scripts/flow-figma-generate.js +683 -0
  68. package/scripts/flow-figma-index.js +909 -0
  69. package/scripts/flow-figma-match.js +617 -0
  70. package/scripts/flow-figma-mcp-server.js +518 -0
  71. package/scripts/flow-figma-pipeline.js +414 -0
  72. package/scripts/flow-file-ops.js +301 -0
  73. package/scripts/flow-gate-confidence.js +825 -0
  74. package/scripts/flow-guided-edit.js +659 -0
  75. package/scripts/flow-health +185 -0
  76. package/scripts/flow-health.js +413 -0
  77. package/scripts/flow-hooks.js +556 -0
  78. package/scripts/flow-http-client.js +249 -0
  79. package/scripts/flow-hybrid-detect.js +167 -0
  80. package/scripts/flow-hybrid-interactive.js +591 -0
  81. package/scripts/flow-hybrid-test.js +152 -0
  82. package/scripts/flow-import-profile +439 -0
  83. package/scripts/flow-init +253 -0
  84. package/scripts/flow-instruction-richness.js +827 -0
  85. package/scripts/flow-jira-integration.js +579 -0
  86. package/scripts/flow-knowledge-router.js +522 -0
  87. package/scripts/flow-knowledge-sync.js +589 -0
  88. package/scripts/flow-linear-integration.js +631 -0
  89. package/scripts/flow-links.js +774 -0
  90. package/scripts/flow-log-manager.js +559 -0
  91. package/scripts/flow-loop-enforcer.js +1246 -0
  92. package/scripts/flow-loop-retry-learning.js +630 -0
  93. package/scripts/flow-lsp.js +923 -0
  94. package/scripts/flow-map-index +348 -0
  95. package/scripts/flow-map-sync +201 -0
  96. package/scripts/flow-memory-blocks.js +668 -0
  97. package/scripts/flow-memory-compactor.js +350 -0
  98. package/scripts/flow-memory-db.js +1110 -0
  99. package/scripts/flow-memory-sync.js +484 -0
  100. package/scripts/flow-metrics.js +353 -0
  101. package/scripts/flow-migrate-ids.js +370 -0
  102. package/scripts/flow-model-adapter.js +802 -0
  103. package/scripts/flow-model-router.js +884 -0
  104. package/scripts/flow-models.js +1231 -0
  105. package/scripts/flow-morning.js +517 -0
  106. package/scripts/flow-multi-approach.js +660 -0
  107. package/scripts/flow-new-feature +86 -0
  108. package/scripts/flow-onboard +1042 -0
  109. package/scripts/flow-orchestrate-llm.js +459 -0
  110. package/scripts/flow-orchestrate.js +3592 -0
  111. package/scripts/flow-output.js +123 -0
  112. package/scripts/flow-parallel-detector.js +399 -0
  113. package/scripts/flow-parallel-dispatch.js +987 -0
  114. package/scripts/flow-parallel.js +428 -0
  115. package/scripts/flow-pattern-enforcer.js +600 -0
  116. package/scripts/flow-prd-manager.js +282 -0
  117. package/scripts/flow-progress.js +323 -0
  118. package/scripts/flow-project-analyzer.js +975 -0
  119. package/scripts/flow-prompt-composer.js +487 -0
  120. package/scripts/flow-providers.js +1381 -0
  121. package/scripts/flow-queue.js +308 -0
  122. package/scripts/flow-ready +82 -0
  123. package/scripts/flow-ready.js +189 -0
  124. package/scripts/flow-regression.js +396 -0
  125. package/scripts/flow-response-parser.js +450 -0
  126. package/scripts/flow-resume.js +284 -0
  127. package/scripts/flow-rules-sync.js +439 -0
  128. package/scripts/flow-run-trace.js +718 -0
  129. package/scripts/flow-safety.js +587 -0
  130. package/scripts/flow-search +104 -0
  131. package/scripts/flow-security.js +481 -0
  132. package/scripts/flow-session-end +106 -0
  133. package/scripts/flow-session-end.js +437 -0
  134. package/scripts/flow-session-state.js +671 -0
  135. package/scripts/flow-setup-hooks +216 -0
  136. package/scripts/flow-setup-hooks.js +377 -0
  137. package/scripts/flow-skill-create.js +329 -0
  138. package/scripts/flow-skill-creator.js +572 -0
  139. package/scripts/flow-skill-generator.js +1046 -0
  140. package/scripts/flow-skill-learn.js +880 -0
  141. package/scripts/flow-skill-matcher.js +578 -0
  142. package/scripts/flow-spec-generator.js +820 -0
  143. package/scripts/flow-stack-wizard.js +895 -0
  144. package/scripts/flow-standup +162 -0
  145. package/scripts/flow-start +74 -0
  146. package/scripts/flow-start.js +235 -0
  147. package/scripts/flow-status +110 -0
  148. package/scripts/flow-status.js +301 -0
  149. package/scripts/flow-step-browser.js +83 -0
  150. package/scripts/flow-step-changelog.js +217 -0
  151. package/scripts/flow-step-comments.js +306 -0
  152. package/scripts/flow-step-complexity.js +234 -0
  153. package/scripts/flow-step-coverage.js +218 -0
  154. package/scripts/flow-step-knowledge.js +193 -0
  155. package/scripts/flow-step-pr-tests.js +364 -0
  156. package/scripts/flow-step-regression.js +89 -0
  157. package/scripts/flow-step-review.js +516 -0
  158. package/scripts/flow-step-security.js +162 -0
  159. package/scripts/flow-step-silent-failures.js +290 -0
  160. package/scripts/flow-step-simplifier.js +346 -0
  161. package/scripts/flow-story +105 -0
  162. package/scripts/flow-story.js +500 -0
  163. package/scripts/flow-suspend.js +252 -0
  164. package/scripts/flow-sync-daemon.js +654 -0
  165. package/scripts/flow-task-analyzer.js +606 -0
  166. package/scripts/flow-team-dashboard.js +748 -0
  167. package/scripts/flow-team-sync.js +752 -0
  168. package/scripts/flow-team.js +977 -0
  169. package/scripts/flow-tech-options.js +528 -0
  170. package/scripts/flow-templates.js +812 -0
  171. package/scripts/flow-tiered-learning.js +728 -0
  172. package/scripts/flow-trace +204 -0
  173. package/scripts/flow-transcript-chunking.js +1106 -0
  174. package/scripts/flow-transcript-digest.js +7918 -0
  175. package/scripts/flow-transcript-language.js +465 -0
  176. package/scripts/flow-transcript-parsing.js +1085 -0
  177. package/scripts/flow-transcript-stories.js +2194 -0
  178. package/scripts/flow-update-map +224 -0
  179. package/scripts/flow-utils.js +2242 -0
  180. package/scripts/flow-verification.js +644 -0
  181. package/scripts/flow-verify.js +1177 -0
  182. package/scripts/flow-voice-input.js +638 -0
  183. package/scripts/flow-watch +168 -0
  184. package/scripts/flow-workflow-steps.js +521 -0
  185. package/scripts/flow-workflow.js +1029 -0
  186. package/scripts/flow-worktree.js +489 -0
  187. package/scripts/hooks/adapters/base-adapter.js +102 -0
  188. package/scripts/hooks/adapters/claude-code.js +359 -0
  189. package/scripts/hooks/adapters/index.js +79 -0
  190. package/scripts/hooks/core/component-check.js +341 -0
  191. package/scripts/hooks/core/index.js +35 -0
  192. package/scripts/hooks/core/loop-check.js +241 -0
  193. package/scripts/hooks/core/session-context.js +294 -0
  194. package/scripts/hooks/core/task-gate.js +177 -0
  195. package/scripts/hooks/core/validation.js +230 -0
  196. package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
  197. package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
  198. package/scripts/hooks/entry/claude-code/session-end.js +87 -0
  199. package/scripts/hooks/entry/claude-code/session-start.js +46 -0
  200. package/scripts/hooks/entry/claude-code/stop.js +43 -0
  201. package/scripts/postinstall.js +139 -0
  202. package/templates/browser-test-flow.json +56 -0
  203. package/templates/bug-report.md +43 -0
  204. package/templates/component-detail.md +42 -0
  205. package/templates/component.stories.tsx +49 -0
  206. package/templates/context/constraints.md +83 -0
  207. package/templates/context/conventions.md +177 -0
  208. package/templates/context/stack.md +60 -0
  209. package/templates/correction-report.md +90 -0
  210. package/templates/feature-proposal.md +35 -0
  211. package/templates/hybrid/_base.md +254 -0
  212. package/templates/hybrid/_patterns.md +45 -0
  213. package/templates/hybrid/create-component.md +127 -0
  214. package/templates/hybrid/create-file.md +56 -0
  215. package/templates/hybrid/create-hook.md +145 -0
  216. package/templates/hybrid/create-service.md +70 -0
  217. package/templates/hybrid/fix-bug.md +33 -0
  218. package/templates/hybrid/modify-file.md +55 -0
  219. package/templates/story.md +68 -0
  220. package/templates/task.json +56 -0
  221. package/templates/trace.md +69 -0
@@ -0,0 +1,85 @@
1
+ #!/bin/bash
2
+
3
+ # Wogi Flow - Generate Changelog
4
+
5
+ set -e
6
+
7
+ WORKFLOW_DIR=".workflow"
8
+ REQUEST_LOG="$WORKFLOW_DIR/state/request-log.md"
9
+ OUTPUT="${1:-CHANGELOG.md}"
10
+
11
+ # Colors
12
+ GREEN='\033[0;32m'
13
+ CYAN='\033[0;36m'
14
+ NC='\033[0m'
15
+
16
+ echo -e "${CYAN}Generating changelog from request-log...${NC}"
17
+ echo ""
18
+
19
+ if [ ! -f "$REQUEST_LOG" ]; then
20
+ echo "No request-log found"
21
+ exit 1
22
+ fi
23
+
24
+ # Generate changelog
25
+ cat > "$OUTPUT" << 'HEADER'
26
+ # Changelog
27
+
28
+ All notable changes to this project, generated from the request log.
29
+
30
+ HEADER
31
+
32
+ python3 << EOF >> "$OUTPUT"
33
+ import re
34
+ from collections import defaultdict
35
+ from datetime import datetime
36
+
37
+ # Parse request log
38
+ entries = defaultdict(list)
39
+
40
+ with open('$REQUEST_LOG') as f:
41
+ content = f.read()
42
+
43
+ # Match entries
44
+ pattern = r'### (R-\d+) \| ([\d-]+ [\d:]+).*?\n\*\*Type\*\*: (\w+).*?\n\*\*Tags\*\*: ([^\n]+).*?\n\*\*Request\*\*: "([^"]+)".*?\n\*\*Result\*\*: ([^\n]+)'
45
+
46
+ for match in re.finditer(pattern, content, re.DOTALL):
47
+ entry_id, timestamp, entry_type, tags, request, result = match.groups()
48
+
49
+ # Parse date
50
+ try:
51
+ date = timestamp.split()[0]
52
+ except:
53
+ date = "Unknown"
54
+
55
+ entries[entry_type].append({
56
+ 'id': entry_id,
57
+ 'date': date,
58
+ 'request': request,
59
+ 'result': result,
60
+ 'tags': tags
61
+ })
62
+
63
+ # Output by type
64
+ type_headers = {
65
+ 'new': 'Added',
66
+ 'change': 'Changed',
67
+ 'fix': 'Fixed',
68
+ 'refactor': 'Refactored'
69
+ }
70
+
71
+ print("## [Unreleased]")
72
+ print()
73
+
74
+ for entry_type, header in type_headers.items():
75
+ if entries[entry_type]:
76
+ print(f"### {header}")
77
+ for entry in entries[entry_type]:
78
+ tags = entry['tags'].replace('#', '').replace(':', '/').strip()
79
+ print(f"- {entry['result']} ({entry['id']})")
80
+ print()
81
+ EOF
82
+
83
+ echo -e "${GREEN}✓${NC} Generated: $OUTPUT"
84
+ echo ""
85
+ echo "Review and edit as needed."
@@ -0,0 +1,483 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Auto-Checkpoint System
5
+ *
6
+ * Creates periodic checkpoints during execution:
7
+ * - Git commits at configurable intervals
8
+ * - State snapshots for rollback
9
+ * - Checkpoint metadata tracking
10
+ *
11
+ * Usage as module:
12
+ * const { Checkpoint, createCheckpoint, rollback } = require('./flow-checkpoint');
13
+ * const cp = new Checkpoint(config);
14
+ * cp.maybeCreate(stepCount);
15
+ *
16
+ * Usage as CLI:
17
+ * flow checkpoint create [message] # Create manual checkpoint
18
+ * flow checkpoint list # List checkpoints
19
+ * flow checkpoint rollback [id] # Rollback to checkpoint
20
+ * flow checkpoint cleanup # Remove old checkpoints
21
+ */
22
+
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+ const { execSync, spawnSync } = require('child_process');
26
+ const { getProjectRoot, getConfig, colors: c } = require('./flow-utils');
27
+
28
+ const PROJECT_ROOT = getProjectRoot();
29
+ const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
30
+ const CHECKPOINTS_DIR = path.join(WORKFLOW_DIR, 'checkpoints');
31
+ const CHECKPOINT_LOG = path.join(CHECKPOINTS_DIR, 'checkpoint-log.json');
32
+
33
+ // Alias getConfig as loadConfig for minimal code changes
34
+ const loadConfig = getConfig;
35
+
36
+ /**
37
+ * Default checkpoint configuration
38
+ */
39
+ const DEFAULT_CHECKPOINT_CONFIG = {
40
+ enabled: true,
41
+ interval: 5,
42
+ maxCheckpoints: 20,
43
+ autoCommit: true,
44
+ commitPrefix: '[checkpoint]',
45
+ includeStateFiles: true,
46
+ excludePatterns: ['node_modules', '.git', 'dist', 'build']
47
+ };
48
+
49
+ /**
50
+ * Checkpoint class for managing automatic checkpoints
51
+ */
52
+ class Checkpoint {
53
+ constructor(config = {}) {
54
+ const cpConfig = config.checkpoint || config.safety?.limits || {};
55
+ this.config = { ...DEFAULT_CHECKPOINT_CONFIG, ...cpConfig };
56
+ this.stepCount = 0;
57
+ this.lastCheckpointStep = 0;
58
+ this.checkpoints = [];
59
+
60
+ this.ensureDir();
61
+ this.loadCheckpoints();
62
+ }
63
+
64
+ /**
65
+ * Ensure checkpoint directory exists
66
+ */
67
+ ensureDir() {
68
+ if (!fs.existsSync(CHECKPOINTS_DIR)) {
69
+ fs.mkdirSync(CHECKPOINTS_DIR, { recursive: true });
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Load existing checkpoints
75
+ */
76
+ loadCheckpoints() {
77
+ if (fs.existsSync(CHECKPOINT_LOG)) {
78
+ try {
79
+ this.checkpoints = JSON.parse(fs.readFileSync(CHECKPOINT_LOG, 'utf-8'));
80
+ } catch {
81
+ this.checkpoints = [];
82
+ }
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Save checkpoint log
88
+ */
89
+ saveCheckpoints() {
90
+ fs.writeFileSync(CHECKPOINT_LOG, JSON.stringify(this.checkpoints, null, 2));
91
+ }
92
+
93
+ /**
94
+ * Check if checkpoint is needed
95
+ */
96
+ needsCheckpoint() {
97
+ if (!this.config.enabled) return false;
98
+ const interval = this.config.interval || 5;
99
+ return this.stepCount > 0 && (this.stepCount - this.lastCheckpointStep) >= interval;
100
+ }
101
+
102
+ /**
103
+ * Increment step counter and maybe create checkpoint
104
+ */
105
+ recordStep() {
106
+ this.stepCount++;
107
+
108
+ if (this.needsCheckpoint()) {
109
+ return this.create(`Auto checkpoint at step ${this.stepCount}`);
110
+ }
111
+
112
+ return null;
113
+ }
114
+
115
+ /**
116
+ * Create a checkpoint
117
+ */
118
+ create(message = null) {
119
+ const timestamp = new Date().toISOString();
120
+ const id = `cp-${timestamp.replace(/[:.]/g, '-').slice(0, 19)}`;
121
+
122
+ const checkpoint = {
123
+ id,
124
+ timestamp,
125
+ step: this.stepCount,
126
+ message: message || `Checkpoint at step ${this.stepCount}`,
127
+ gitCommit: null,
128
+ stateSnapshot: null,
129
+ filesTracked: []
130
+ };
131
+
132
+ // Snapshot state files
133
+ if (this.config.includeStateFiles) {
134
+ checkpoint.stateSnapshot = this.snapshotState(id);
135
+ }
136
+
137
+ // Git commit if enabled
138
+ if (this.config.autoCommit && this.hasGitChanges()) {
139
+ checkpoint.gitCommit = this.createGitCommit(checkpoint.message);
140
+ }
141
+
142
+ this.checkpoints.push(checkpoint);
143
+ this.lastCheckpointStep = this.stepCount;
144
+ this.saveCheckpoints();
145
+
146
+ // Cleanup old checkpoints
147
+ this.cleanup();
148
+
149
+ return checkpoint;
150
+ }
151
+
152
+ /**
153
+ * Snapshot workflow state files
154
+ */
155
+ snapshotState(id) {
156
+ const snapshotDir = path.join(CHECKPOINTS_DIR, id);
157
+ if (!fs.existsSync(snapshotDir)) {
158
+ fs.mkdirSync(snapshotDir, { recursive: true });
159
+ }
160
+
161
+ const stateFiles = [
162
+ 'state/ready.json',
163
+ 'state/request-log.md',
164
+ 'state/app-map.md',
165
+ 'state/decisions.md',
166
+ 'state/progress.md',
167
+ 'state/durable-session.json',
168
+ 'config.json'
169
+ ];
170
+
171
+ const snapshots = {};
172
+
173
+ for (const relPath of stateFiles) {
174
+ const srcPath = path.join(WORKFLOW_DIR, relPath);
175
+ if (fs.existsSync(srcPath)) {
176
+ const content = fs.readFileSync(srcPath, 'utf-8');
177
+ const destPath = path.join(snapshotDir, relPath.replace(/\//g, '_'));
178
+ fs.writeFileSync(destPath, content);
179
+ snapshots[relPath] = destPath;
180
+ }
181
+ }
182
+
183
+ return snapshots;
184
+ }
185
+
186
+ /**
187
+ * Check if there are git changes to commit
188
+ */
189
+ hasGitChanges() {
190
+ try {
191
+ const result = spawnSync('git', ['status', '--porcelain'], {
192
+ cwd: PROJECT_ROOT,
193
+ encoding: 'utf-8'
194
+ });
195
+ return result.stdout && result.stdout.trim().length > 0;
196
+ } catch {
197
+ return false;
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Create a git commit
203
+ */
204
+ createGitCommit(message) {
205
+ try {
206
+ // Stage all changes
207
+ spawnSync('git', ['add', '-A'], { cwd: PROJECT_ROOT });
208
+
209
+ // Create commit
210
+ const commitMessage = `${this.config.commitPrefix} ${message}`;
211
+ const result = spawnSync('git', ['commit', '-m', commitMessage], {
212
+ cwd: PROJECT_ROOT,
213
+ encoding: 'utf-8'
214
+ });
215
+
216
+ if (result.status === 0) {
217
+ // Get commit hash
218
+ const hashResult = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {
219
+ cwd: PROJECT_ROOT,
220
+ encoding: 'utf-8'
221
+ });
222
+ return hashResult.stdout.trim();
223
+ }
224
+ } catch {
225
+ // Git commit failed
226
+ }
227
+ return null;
228
+ }
229
+
230
+ /**
231
+ * Rollback to a checkpoint
232
+ */
233
+ rollback(checkpointId) {
234
+ const checkpoint = this.checkpoints.find(cp => cp.id === checkpointId);
235
+ if (!checkpoint) {
236
+ throw new Error(`Checkpoint not found: ${checkpointId}`);
237
+ }
238
+
239
+ const results = {
240
+ stateRestored: false,
241
+ gitRestored: false,
242
+ errors: []
243
+ };
244
+
245
+ // Restore state files
246
+ if (checkpoint.stateSnapshot) {
247
+ try {
248
+ for (const [relPath, snapshotPath] of Object.entries(checkpoint.stateSnapshot)) {
249
+ if (fs.existsSync(snapshotPath)) {
250
+ const destPath = path.join(WORKFLOW_DIR, relPath);
251
+ const content = fs.readFileSync(snapshotPath, 'utf-8');
252
+ fs.writeFileSync(destPath, content);
253
+ }
254
+ }
255
+ results.stateRestored = true;
256
+ } catch (err) {
257
+ results.errors.push(`Failed to restore state: ${err.message}`);
258
+ }
259
+ }
260
+
261
+ // Rollback git if commit exists
262
+ if (checkpoint.gitCommit) {
263
+ try {
264
+ // Find commits since checkpoint
265
+ const logResult = spawnSync('git', [
266
+ 'log', '--oneline', `${checkpoint.gitCommit}..HEAD`
267
+ ], { cwd: PROJECT_ROOT, encoding: 'utf-8' });
268
+
269
+ const commitsSince = logResult.stdout.trim().split('\n').filter(l => l).length;
270
+
271
+ if (commitsSince > 0) {
272
+ // Soft reset to checkpoint
273
+ spawnSync('git', ['reset', '--soft', checkpoint.gitCommit], {
274
+ cwd: PROJECT_ROOT
275
+ });
276
+ results.gitRestored = true;
277
+ }
278
+ } catch (err) {
279
+ results.errors.push(`Failed to rollback git: ${err.message}`);
280
+ }
281
+ }
282
+
283
+ return results;
284
+ }
285
+
286
+ /**
287
+ * Cleanup old checkpoints
288
+ */
289
+ cleanup() {
290
+ const max = this.config.maxCheckpoints || 20;
291
+
292
+ while (this.checkpoints.length > max) {
293
+ const oldest = this.checkpoints.shift();
294
+
295
+ // Remove snapshot directory
296
+ if (oldest.stateSnapshot) {
297
+ const snapshotDir = path.join(CHECKPOINTS_DIR, oldest.id);
298
+ if (fs.existsSync(snapshotDir)) {
299
+ fs.rmSync(snapshotDir, { recursive: true });
300
+ }
301
+ }
302
+ }
303
+
304
+ this.saveCheckpoints();
305
+ return this.checkpoints.length;
306
+ }
307
+
308
+ /**
309
+ * Get checkpoint list
310
+ */
311
+ list() {
312
+ return this.checkpoints.map(cp => ({
313
+ id: cp.id,
314
+ timestamp: cp.timestamp,
315
+ step: cp.step,
316
+ message: cp.message,
317
+ hasGitCommit: !!cp.gitCommit,
318
+ hasStateSnapshot: !!cp.stateSnapshot
319
+ }));
320
+ }
321
+
322
+ /**
323
+ * Get current status
324
+ */
325
+ getStatus() {
326
+ return {
327
+ enabled: this.config.enabled,
328
+ interval: this.config.interval,
329
+ stepCount: this.stepCount,
330
+ lastCheckpointStep: this.lastCheckpointStep,
331
+ totalCheckpoints: this.checkpoints.length,
332
+ nextCheckpointAt: this.lastCheckpointStep + this.config.interval
333
+ };
334
+ }
335
+ }
336
+
337
+
338
+ /**
339
+ * Format checkpoint list for display
340
+ */
341
+ function formatCheckpointList(checkpoints) {
342
+ if (checkpoints.length === 0) {
343
+ return `${c.dim}No checkpoints found${c.reset}`;
344
+ }
345
+
346
+ let output = `${c.cyan}${c.bold}Checkpoints${c.reset}\n`;
347
+ output += `${'─'.repeat(60)}\n\n`;
348
+
349
+ for (const cp of checkpoints.reverse()) {
350
+ const date = new Date(cp.timestamp);
351
+ const timeStr = date.toLocaleString();
352
+
353
+ output += `${c.bold}${cp.id}${c.reset}\n`;
354
+ output += ` ${c.dim}Time:${c.reset} ${timeStr}\n`;
355
+ output += ` ${c.dim}Step:${c.reset} ${cp.step}\n`;
356
+ output += ` ${c.dim}Message:${c.reset} ${cp.message}\n`;
357
+
358
+ const features = [];
359
+ if (cp.hasGitCommit) features.push('git');
360
+ if (cp.hasStateSnapshot) features.push('state');
361
+ output += ` ${c.dim}Includes:${c.reset} ${features.join(', ') || 'none'}\n`;
362
+ output += '\n';
363
+ }
364
+
365
+ return output;
366
+ }
367
+
368
+ // Module exports
369
+ module.exports = {
370
+ Checkpoint,
371
+ DEFAULT_CHECKPOINT_CONFIG,
372
+ loadConfig
373
+ };
374
+
375
+ // CLI Handler
376
+ if (require.main === module) {
377
+ const args = process.argv.slice(2);
378
+ const command = args[0];
379
+
380
+ const config = loadConfig();
381
+ const cp = new Checkpoint(config);
382
+
383
+ switch (command) {
384
+ case 'create': {
385
+ const message = args.slice(1).join(' ') || 'Manual checkpoint';
386
+ console.log(`${c.cyan}Creating checkpoint...${c.reset}`);
387
+ const checkpoint = cp.create(message);
388
+ console.log(`${c.green}✅ Checkpoint created: ${checkpoint.id}${c.reset}`);
389
+ if (checkpoint.gitCommit) {
390
+ console.log(` Git commit: ${checkpoint.gitCommit}`);
391
+ }
392
+ break;
393
+ }
394
+
395
+ case 'list': {
396
+ const checkpoints = cp.list();
397
+ console.log(formatCheckpointList(checkpoints));
398
+ break;
399
+ }
400
+
401
+ case 'rollback': {
402
+ const checkpointId = args[1];
403
+ if (!checkpointId) {
404
+ console.error(`${c.red}Error: Checkpoint ID required${c.reset}`);
405
+ console.log(`${c.dim}Usage: flow checkpoint rollback <id>${c.reset}`);
406
+ process.exit(1);
407
+ }
408
+
409
+ console.log(`${c.yellow}Rolling back to ${checkpointId}...${c.reset}`);
410
+ try {
411
+ const results = cp.rollback(checkpointId);
412
+
413
+ if (results.stateRestored) {
414
+ console.log(`${c.green}✅ State files restored${c.reset}`);
415
+ }
416
+ if (results.gitRestored) {
417
+ console.log(`${c.green}✅ Git rolled back${c.reset}`);
418
+ }
419
+ if (results.errors.length > 0) {
420
+ for (const err of results.errors) {
421
+ console.log(`${c.yellow}⚠ ${err}${c.reset}`);
422
+ }
423
+ }
424
+ } catch (err) {
425
+ console.error(`${c.red}Error: ${err.message}${c.reset}`);
426
+ process.exit(1);
427
+ }
428
+ break;
429
+ }
430
+
431
+ case 'cleanup': {
432
+ const remaining = cp.cleanup();
433
+ console.log(`${c.green}✅ Cleanup complete. ${remaining} checkpoints remaining${c.reset}`);
434
+ break;
435
+ }
436
+
437
+ case 'status': {
438
+ const status = cp.getStatus();
439
+ console.log(`${c.cyan}Checkpoint Status${c.reset}`);
440
+ console.log(`${'─'.repeat(40)}`);
441
+ console.log(` Enabled: ${status.enabled ? c.green + 'Yes' : c.yellow + 'No'}${c.reset}`);
442
+ console.log(` Interval: Every ${status.interval} steps`);
443
+ console.log(` Current step: ${status.stepCount}`);
444
+ console.log(` Last checkpoint: Step ${status.lastCheckpointStep}`);
445
+ console.log(` Total checkpoints: ${status.totalCheckpoints}`);
446
+ break;
447
+ }
448
+
449
+ default: {
450
+ console.log(`
451
+ ${c.cyan}Wogi Flow - Auto-Checkpoint System${c.reset}
452
+
453
+ ${c.bold}Usage:${c.reset}
454
+ flow checkpoint create [message] Create manual checkpoint
455
+ flow checkpoint list List all checkpoints
456
+ flow checkpoint rollback <id> Rollback to checkpoint
457
+ flow checkpoint cleanup Remove old checkpoints
458
+ flow checkpoint status Show checkpoint status
459
+
460
+ ${c.bold}Configuration:${c.reset}
461
+ Add to .workflow/config.json:
462
+ {
463
+ "checkpoint": {
464
+ "enabled": true,
465
+ "interval": 5,
466
+ "maxCheckpoints": 20,
467
+ "autoCommit": true,
468
+ "commitPrefix": "[checkpoint]"
469
+ }
470
+ }
471
+
472
+ Or in safety.limits:
473
+ {
474
+ "safety": {
475
+ "limits": {
476
+ "checkpointInterval": 5
477
+ }
478
+ }
479
+ }
480
+ `);
481
+ }
482
+ }
483
+ }