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,151 @@
1
+ #!/bin/bash
2
+
3
+ # Wogi Flow - Complete Task
4
+
5
+ set -e
6
+
7
+ WORKFLOW_DIR=".workflow"
8
+ READY_JSON="$WORKFLOW_DIR/state/ready.json"
9
+ CONFIG_JSON="$WORKFLOW_DIR/config.json"
10
+ REQUEST_LOG="$WORKFLOW_DIR/state/request-log.md"
11
+
12
+ # Colors
13
+ GREEN='\033[0;32m'
14
+ YELLOW='\033[1;33m'
15
+ RED='\033[0;31m'
16
+ NC='\033[0m'
17
+
18
+ if [ -z "$1" ]; then
19
+ echo "Usage: flow done <task-id> [commit-message]"
20
+ exit 1
21
+ fi
22
+
23
+ TASK_ID="$1"
24
+ COMMIT_MSG="${2:-Complete $TASK_ID}"
25
+
26
+ if [ ! -f "$READY_JSON" ]; then
27
+ echo -e "${RED}Error: No ready.json found${NC}"
28
+ exit 1
29
+ fi
30
+
31
+ # Check quality gates from config
32
+ gates_passed=true
33
+
34
+ if [ -f "$CONFIG_JSON" ]; then
35
+ echo -e "${YELLOW}Running quality gates...${NC}"
36
+ echo ""
37
+
38
+ # Parse config and run gates
39
+ CONFIG_FILE="$CONFIG_JSON" REQUEST_LOG_FILE="$REQUEST_LOG" TASK_ID_ARG="$TASK_ID" python3 << 'GATES'
40
+ import json
41
+ import subprocess
42
+ import sys
43
+ import os
44
+
45
+ with open(os.environ['CONFIG_FILE']) as f:
46
+ config = json.load(f)
47
+
48
+ task_id = os.environ['TASK_ID_ARG']
49
+ request_log = os.environ['REQUEST_LOG_FILE']
50
+ gates = config.get('qualityGates', {}).get('feature', {}).get('require', [])
51
+ testing = config.get('testing', {})
52
+ failed = []
53
+
54
+ for gate in gates:
55
+ if gate == 'tests':
56
+ if testing.get('runAfterTask', False) or testing.get('runBeforeCommit', False):
57
+ print(' Running tests...')
58
+ result = subprocess.run(['npm', 'test'], capture_output=True, text=True)
59
+ if result.returncode == 0:
60
+ print(' \033[0;32m✓\033[0m tests passed')
61
+ else:
62
+ print(' \033[0;31m✗\033[0m tests failed')
63
+ failed.append('tests')
64
+ else:
65
+ print(' \033[0;33m○\033[0m tests (not configured to run)')
66
+
67
+ elif gate == 'requestLogEntry':
68
+ # Check if request-log has an entry for this task
69
+ try:
70
+ with open(request_log) as f:
71
+ content = f.read()
72
+ if task_id in content or content.strip().endswith('---'):
73
+ print(' \033[0;33m○\033[0m requestLogEntry (verify manually)')
74
+ else:
75
+ print(' \033[0;33m○\033[0m requestLogEntry (verify manually)')
76
+ except:
77
+ print(' \033[0;33m○\033[0m requestLogEntry (verify manually)')
78
+
79
+ elif gate == 'appMapUpdate':
80
+ print(' \033[0;33m○\033[0m appMapUpdate (verify manually if components created)')
81
+
82
+ else:
83
+ print(f' \033[0;33m○\033[0m {gate} (manual check)')
84
+
85
+ if failed:
86
+ print(f'\n\033[0;31mFailed gates: {", ".join(failed)}\033[0m')
87
+ sys.exit(1)
88
+ GATES
89
+
90
+ if [ $? -ne 0 ]; then
91
+ echo -e "${RED}Quality gates failed. Fix issues before completing.${NC}"
92
+ exit 1
93
+ fi
94
+ echo ""
95
+ fi
96
+
97
+ READY_FILE="$READY_JSON" TASK_ID_ARG="$TASK_ID" python3 << 'EOF'
98
+ import json, os
99
+
100
+ task_id = os.environ['TASK_ID_ARG']
101
+ ready_file = os.environ['READY_FILE']
102
+
103
+ with open(ready_file) as f:
104
+ data = json.load(f)
105
+
106
+ # Find task in inProgress
107
+ in_progress = data.get('inProgress', [])
108
+ found = None
109
+ found_idx = None
110
+
111
+ for i, task in enumerate(in_progress):
112
+ if isinstance(task, dict):
113
+ if task.get('id') == task_id:
114
+ found = task
115
+ found_idx = i
116
+ break
117
+ elif task == task_id:
118
+ found = {'id': task_id}
119
+ found_idx = i
120
+ break
121
+
122
+ if found is None:
123
+ print(f'\033[0;31mTask {task_id} not found in progress\033[0m')
124
+ exit(1)
125
+
126
+ # Move to completed
127
+ in_progress.pop(found_idx)
128
+ data['inProgress'] = in_progress
129
+
130
+ completed = data.get('recentlyCompleted', [])
131
+ completed.insert(0, found)
132
+ data['recentlyCompleted'] = completed[:10] # Keep last 10
133
+
134
+ # Update timestamp
135
+ from datetime import datetime
136
+ data['lastUpdated'] = datetime.now().isoformat()
137
+
138
+ with open(ready_file, 'w') as f:
139
+ json.dump(data, f, indent=2)
140
+
141
+ print(f'\033[0;32m✓ Completed: {task_id}\033[0m')
142
+ EOF
143
+
144
+ # Commit if there are changes
145
+ if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
146
+ echo ""
147
+ echo -e "${YELLOW}Committing changes...${NC}"
148
+ git add -A
149
+ git commit -m "feat: $COMMIT_MSG"
150
+ echo -e "${GREEN}✓ Changes committed${NC}"
151
+ fi
@@ -0,0 +1,489 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Complete Task
5
+ *
6
+ * Runs quality gates and moves task from inProgress to completed.
7
+ */
8
+
9
+ const { execSync, execFileSync, spawnSync } = require('child_process');
10
+ const path = require('path');
11
+ const {
12
+ PATHS,
13
+ fileExists,
14
+ getConfig,
15
+ moveTaskAsync,
16
+ findTask,
17
+ readFile,
18
+ writeJson,
19
+ color,
20
+ success,
21
+ warn,
22
+ error
23
+ } = require('./flow-utils');
24
+
25
+ // v1.7.0 context memory management
26
+ const { warnIfContextHigh } = require('./flow-context-monitor');
27
+ const { clearCurrentTask, addKeyFact } = require('./flow-memory-blocks');
28
+ const { trackTaskComplete } = require('./flow-session-state');
29
+ const { autoArchiveIfNeeded } = require('./flow-log-manager');
30
+
31
+ // v1.9.0 regression testing and browser test suggestions (legacy - now in workflow steps)
32
+ const { runRegressionTests } = require('./flow-regression');
33
+ const { suggestBrowserTests } = require('./flow-browser-suggest');
34
+
35
+ // v2.2 modular workflow steps
36
+ const { runSteps, getAllSteps } = require('./flow-workflow-steps');
37
+
38
+ // v2.0 durable session support
39
+ const { loadDurableSession, archiveDurableSession } = require('./flow-durable-session');
40
+
41
+ // v2.1 loop enforcement as explicit quality gate
42
+ const { canExitLoop, getActiveLoop } = require('./flow-loop-enforcer');
43
+
44
+ // Path for last failure artifact
45
+ const LAST_FAILURE_PATH = path.join(PATHS.state, 'last-failure.json');
46
+
47
+ /**
48
+ * Get files modified in current task (from git)
49
+ */
50
+ function getModifiedFiles() {
51
+ try {
52
+ // Get staged and unstaged changes
53
+ const staged = execSync('git diff --cached --name-only', {
54
+ encoding: 'utf-8',
55
+ stdio: ['pipe', 'pipe', 'pipe']
56
+ }).trim().split('\n').filter(Boolean);
57
+
58
+ const unstaged = execSync('git diff --name-only', {
59
+ encoding: 'utf-8',
60
+ stdio: ['pipe', 'pipe', 'pipe']
61
+ }).trim().split('\n').filter(Boolean);
62
+
63
+ const untracked = execSync('git ls-files --others --exclude-standard', {
64
+ encoding: 'utf-8',
65
+ stdio: ['pipe', 'pipe', 'pipe']
66
+ }).trim().split('\n').filter(Boolean);
67
+
68
+ // Combine and dedupe
69
+ const all = [...new Set([...staged, ...unstaged, ...untracked])];
70
+ return all.filter(f => f && f.length > 0);
71
+ } catch (err) {
72
+ return [];
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Truncate error output to reasonable length
78
+ */
79
+ function truncateOutput(text, maxLines = 30, maxChars = 2000) {
80
+ if (!text) return '';
81
+ const lines = text.split('\n').slice(0, maxLines);
82
+ let result = lines.join('\n');
83
+ if (result.length > maxChars) {
84
+ result = result.substring(0, maxChars) + '\n... (truncated)';
85
+ }
86
+ return result;
87
+ }
88
+
89
+ /**
90
+ * Run quality gates from config
91
+ */
92
+ function runQualityGates(taskId) {
93
+ if (!fileExists(PATHS.config)) {
94
+ return { passed: true, failed: [], errors: {} };
95
+ }
96
+
97
+ console.log(color('yellow', 'Running quality gates...'));
98
+ console.log('');
99
+
100
+ const config = getConfig();
101
+ const gates = config.qualityGates?.feature?.require || [];
102
+ const testing = config.testing || {};
103
+ const failed = [];
104
+ const errors = {}; // Store error output for correction artifact
105
+
106
+ for (const gate of gates) {
107
+ if (gate === 'tests') {
108
+ if (testing.runAfterTask || testing.runBeforeCommit) {
109
+ console.log(' Running tests...');
110
+ const result = spawnSync('npm', ['test'], {
111
+ encoding: 'utf-8',
112
+ stdio: ['pipe', 'pipe', 'pipe']
113
+ });
114
+ if (result.status === 0) {
115
+ console.log(` ${color('green', '✓')} tests passed`);
116
+ } else {
117
+ console.log(` ${color('red', '✗')} tests failed`);
118
+ // Capture error output
119
+ const errorOutput = result.stderr || result.stdout || '';
120
+ if (errorOutput) {
121
+ console.log(color('dim', ' Error output:'));
122
+ const truncated = truncateOutput(errorOutput, 20, 1000);
123
+ truncated.split('\n').forEach(line => {
124
+ console.log(color('dim', ` ${line}`));
125
+ });
126
+ }
127
+ errors.tests = errorOutput;
128
+ failed.push('tests');
129
+ }
130
+ } else {
131
+ console.log(` ${color('yellow', '○')} tests (not configured to run)`);
132
+ }
133
+ } else if (gate === 'lint') {
134
+ console.log(' Running lint...');
135
+ let result = spawnSync('npm', ['run', 'lint'], {
136
+ encoding: 'utf-8',
137
+ stdio: ['pipe', 'pipe', 'pipe']
138
+ });
139
+
140
+ if (result.status !== 0) {
141
+ // Try auto-fix
142
+ console.log(` ${color('yellow', '⟳')} lint issues found, attempting auto-fix...`);
143
+ const fixResult = spawnSync('npm', ['run', 'lint', '--', '--fix'], {
144
+ encoding: 'utf-8',
145
+ stdio: ['pipe', 'pipe', 'pipe']
146
+ });
147
+
148
+ // Re-run lint to check if issues are fixed
149
+ result = spawnSync('npm', ['run', 'lint'], {
150
+ encoding: 'utf-8',
151
+ stdio: ['pipe', 'pipe', 'pipe']
152
+ });
153
+
154
+ if (result.status === 0) {
155
+ console.log(` ${color('green', '✓')} lint passed (auto-fixed)`);
156
+ } else {
157
+ console.log(` ${color('red', '✗')} lint failed (manual fix required)`);
158
+ const errorOutput = result.stderr || result.stdout || '';
159
+ if (errorOutput) {
160
+ console.log(color('dim', ' Remaining issues:'));
161
+ const truncated = truncateOutput(errorOutput, 15, 800);
162
+ truncated.split('\n').forEach(line => {
163
+ console.log(color('dim', ` ${line}`));
164
+ });
165
+ }
166
+ errors.lint = errorOutput;
167
+ failed.push('lint');
168
+ }
169
+ } else {
170
+ console.log(` ${color('green', '✓')} lint passed`);
171
+ }
172
+ } else if (gate === 'typecheck') {
173
+ console.log(' Running typecheck...');
174
+ const result = spawnSync('npm', ['run', 'typecheck'], {
175
+ encoding: 'utf-8',
176
+ stdio: ['pipe', 'pipe', 'pipe']
177
+ });
178
+ if (result.status === 0) {
179
+ console.log(` ${color('green', '✓')} typecheck passed`);
180
+ } else {
181
+ console.log(` ${color('red', '✗')} typecheck failed`);
182
+ const errorOutput = result.stderr || result.stdout || '';
183
+ if (errorOutput) {
184
+ console.log(color('dim', ' Type errors:'));
185
+ const truncated = truncateOutput(errorOutput, 20, 1000);
186
+ truncated.split('\n').forEach(line => {
187
+ console.log(color('dim', ` ${line}`));
188
+ });
189
+ }
190
+ errors.typecheck = errorOutput;
191
+ failed.push('typecheck');
192
+ }
193
+ } else if (gate === 'requestLogEntry') {
194
+ // Check if request-log has an entry for this task
195
+ try {
196
+ const content = readFile(PATHS.requestLog, '');
197
+ if (content.includes(taskId)) {
198
+ console.log(` ${color('green', '✓')} requestLogEntry (found in request-log)`);
199
+ } else {
200
+ console.log(` ${color('yellow', '○')} requestLogEntry (add entry to request-log.md)`);
201
+ }
202
+ } catch (err) {
203
+ if (process.env.DEBUG) console.error(`[DEBUG] requestLogEntry check: ${err.message}`);
204
+ console.log(` ${color('yellow', '○')} requestLogEntry (could not check)`);
205
+ }
206
+ } else if (gate === 'appMapUpdate') {
207
+ console.log(` ${color('yellow', '○')} appMapUpdate (verify manually if components created)`);
208
+ } else if (gate === 'loopComplete') {
209
+ // v2.1: Explicit loop completion check
210
+ const activeLoop = getActiveLoop();
211
+ if (!activeLoop) {
212
+ // No active loop - either completed or not used
213
+ console.log(` ${color('green', '✓')} loopComplete (no active loop session)`);
214
+ } else {
215
+ const exitResult = canExitLoop();
216
+ if (exitResult.canExit) {
217
+ console.log(` ${color('green', '✓')} loopComplete (${exitResult.reason})`);
218
+ } else {
219
+ console.log(` ${color('red', '✗')} loopComplete (${exitResult.pending || 0} pending, ${exitResult.failed || 0} failed)`);
220
+ errors.loopComplete = exitResult.message || 'Loop not complete';
221
+ failed.push('loopComplete');
222
+ }
223
+ }
224
+ } else if (gate === 'noNewFeatures') {
225
+ // Refactor-specific gate - manual check
226
+ console.log(` ${color('yellow', '○')} noNewFeatures (verify no behavior changes)`);
227
+ } else {
228
+ console.log(` ${color('yellow', '○')} ${gate} (manual check)`);
229
+ }
230
+ }
231
+
232
+ if (failed.length > 0) {
233
+ console.log('');
234
+ console.log(color('red', `Failed gates: ${failed.join(', ')}`));
235
+ }
236
+
237
+ return { passed: failed.length === 0, failed, errors };
238
+ }
239
+
240
+ /**
241
+ * Commit changes if any
242
+ */
243
+ function commitChanges(commitMsg) {
244
+ try {
245
+ const status = execSync('git status --porcelain', {
246
+ encoding: 'utf-8',
247
+ stdio: ['pipe', 'pipe', 'pipe']
248
+ });
249
+
250
+ if (status.trim()) {
251
+ console.log('');
252
+ console.log(color('yellow', 'Committing changes...'));
253
+ // Use execFileSync to prevent command injection
254
+ execFileSync('git', ['add', '-A'], { stdio: 'pipe' });
255
+ execFileSync('git', ['commit', '-m', `feat: ${commitMsg}`], { stdio: 'pipe' });
256
+ success('Changes committed');
257
+ }
258
+ } catch (err) {
259
+ // Log git errors but don't fail the task completion
260
+ warn(`Git operation skipped: ${err.message || 'not a git repo or no changes'}`);
261
+ }
262
+ }
263
+
264
+ async function main() {
265
+ const taskId = process.argv[2];
266
+ const commitMsg = process.argv[3] || `Complete ${taskId}`;
267
+
268
+ if (!taskId) {
269
+ console.log('Usage: flow done <task-id> [commit-message]');
270
+ process.exit(1);
271
+ }
272
+
273
+ if (!fileExists(PATHS.ready)) {
274
+ error('No ready.json found');
275
+ process.exit(1);
276
+ }
277
+
278
+ // Run quality gates
279
+ const gateResult = runQualityGates(taskId);
280
+
281
+ if (!gateResult.passed) {
282
+ // Create correction artifact for AI self-repair
283
+ try {
284
+ writeJson(LAST_FAILURE_PATH, {
285
+ taskId,
286
+ timestamp: new Date().toISOString(),
287
+ failedGates: gateResult.failed,
288
+ errors: gateResult.errors
289
+ });
290
+ console.log('');
291
+ console.log(color('dim', `Failure details saved to: ${LAST_FAILURE_PATH}`));
292
+ } catch (err) {
293
+ if (process.env.DEBUG) console.error(`[DEBUG] Failed to save failure artifact: ${err.message}`);
294
+ }
295
+
296
+ console.log('');
297
+ error('Quality gates failed. Fix issues before completing.');
298
+ console.log(color('dim', 'Tip: Review the error output above or check .workflow/state/last-failure.json'));
299
+ process.exit(1);
300
+ }
301
+
302
+ console.log('');
303
+
304
+ // Check if task exists
305
+ const found = findTask(taskId);
306
+
307
+ if (!found) {
308
+ console.log(color('red', `Task ${taskId} not found in any queue`));
309
+ process.exit(1);
310
+ }
311
+
312
+ if (found.list !== 'inProgress') {
313
+ console.log(color('red', `Task ${taskId} is in ${found.list}, not inProgress`));
314
+ process.exit(1);
315
+ }
316
+
317
+ // Move task from inProgress to recentlyCompleted (with file locking)
318
+ const result = await moveTaskAsync(taskId, 'inProgress', 'recentlyCompleted');
319
+
320
+ if (!result.success) {
321
+ error(result.error);
322
+ process.exit(1);
323
+ }
324
+
325
+ console.log(color('green', `✓ Completed: ${taskId}`));
326
+
327
+ // v2.0: Archive durable session if one exists for this task
328
+ try {
329
+ const durableSession = loadDurableSession();
330
+ if (durableSession && durableSession.taskId === taskId) {
331
+ const archived = archiveDurableSession('completed');
332
+ if (archived && process.env.DEBUG) {
333
+ console.log(color('dim', `Archived durable session: ${archived.metrics.stepsCompleted} steps completed`));
334
+ }
335
+ }
336
+ } catch (err) {
337
+ if (process.env.DEBUG) console.error(`[DEBUG] Durable session archive: ${err.message}`);
338
+ }
339
+
340
+ // v1.7.0: Track task completion in session state and memory blocks
341
+ try {
342
+ trackTaskComplete(taskId);
343
+ clearCurrentTask();
344
+
345
+ // Add completion as a key fact
346
+ const taskTitle = result.task?.title || taskId;
347
+ addKeyFact(`Completed: ${taskTitle}`);
348
+ } catch (err) {
349
+ if (process.env.DEBUG) console.error(`[DEBUG] Task tracking: ${err.message}`);
350
+ }
351
+
352
+ // v1.7.0: Auto-archive request log if threshold exceeded
353
+ try {
354
+ const archiveResult = autoArchiveIfNeeded();
355
+ if (archiveResult && archiveResult.archived > 0) {
356
+ success(`Archived ${archiveResult.archived} request log entries`);
357
+ }
358
+ } catch (err) {
359
+ if (process.env.DEBUG) console.error(`[DEBUG] Auto-archive: ${err.message}`);
360
+ }
361
+
362
+ // v2.2: Run afterTask workflow steps
363
+ const modifiedFiles = getModifiedFiles();
364
+ const taskTitle = result.task?.title || taskId;
365
+ const taskType = result.task?.type || 'feature';
366
+
367
+ try {
368
+ const allSteps = getAllSteps();
369
+ const hasAfterTaskSteps = Object.values(allSteps).some(s => s.enabled && s.when === 'afterTask');
370
+
371
+ if (hasAfterTaskSteps) {
372
+ console.log('');
373
+ console.log(color('cyan', 'Running afterTask workflow steps...'));
374
+ const afterTaskResult = await runSteps('afterTask', {
375
+ taskId,
376
+ taskTitle,
377
+ taskType,
378
+ files: modifiedFiles,
379
+ });
380
+
381
+ if (afterTaskResult.blocked) {
382
+ error('Workflow step blocked task completion');
383
+ process.exit(1);
384
+ }
385
+ }
386
+ } catch (err) {
387
+ if (process.env.DEBUG) console.error(`[DEBUG] afterTask steps: ${err.message}`);
388
+ }
389
+
390
+ // Auto-capture learnings from bug fixes
391
+ if (taskType === 'bugfix' || taskType === 'fix') {
392
+ try {
393
+ const { captureFromBugFix } = require('./flow-auto-learn');
394
+ captureFromBugFix(taskId, modifiedFiles, taskTitle);
395
+ } catch (err) {
396
+ if (process.env.DEBUG) console.error(`[DEBUG] auto-learn: ${err.message}`);
397
+ }
398
+ }
399
+
400
+ // v2.2: Run beforeCommit workflow steps
401
+ try {
402
+ const allSteps = getAllSteps();
403
+ const hasBeforeCommitSteps = Object.values(allSteps).some(s => s.enabled && s.when === 'beforeCommit');
404
+
405
+ if (hasBeforeCommitSteps) {
406
+ console.log('');
407
+ console.log(color('cyan', 'Running beforeCommit workflow steps...'));
408
+ const beforeCommitResult = await runSteps('beforeCommit', {
409
+ taskId,
410
+ taskTitle,
411
+ taskType,
412
+ files: modifiedFiles,
413
+ });
414
+
415
+ if (beforeCommitResult.blocked) {
416
+ error('Workflow step blocked commit');
417
+ process.exit(1);
418
+ }
419
+ }
420
+ } catch (err) {
421
+ if (process.env.DEBUG) console.error(`[DEBUG] beforeCommit steps: ${err.message}`);
422
+ }
423
+
424
+ // Commit if there are changes
425
+ commitChanges(commitMsg);
426
+
427
+ // v1.9.0: Run regression tests if configured (legacy - skipped if using workflowSteps)
428
+ const config = getConfig();
429
+ const usingWorkflowSteps = config.workflowSteps?.regressionTest?.enabled;
430
+ if (!usingWorkflowSteps && config.regressionTesting?.enabled && config.regressionTesting?.runOnTaskComplete) {
431
+ console.log('');
432
+ try {
433
+ const regressionResult = await runRegressionTests({ force: true });
434
+ if (!regressionResult.success && config.regressionTesting?.onFailure === 'block') {
435
+ warn('Regression tests failed - review before continuing');
436
+ process.exit(1);
437
+ } else if (!regressionResult.success) {
438
+ warn('Regression tests failed - consider reviewing');
439
+ }
440
+ } catch (err) {
441
+ if (process.env.DEBUG) console.error(`[DEBUG] Regression tests: ${err.message}`);
442
+ }
443
+ }
444
+
445
+ // v1.9.0: Suggest browser tests for UI tasks (legacy - skipped if using workflowSteps)
446
+ const usingBrowserWorkflowStep = config.workflowSteps?.browserTest?.enabled;
447
+ if (!usingBrowserWorkflowStep && config.browserTesting?.enabled && config.browserTesting?.runOnTaskComplete) {
448
+ try {
449
+ const browserSuggestion = suggestBrowserTests(taskId, result.task);
450
+ if (browserSuggestion.suggested && browserSuggestion.flows.length > 0) {
451
+ console.log('');
452
+ console.log(color('cyan', '🌐 Browser tests available:'));
453
+ browserSuggestion.flows.forEach(flow => {
454
+ console.log(color('dim', ` - ${flow}`));
455
+ });
456
+ console.log(color('dim', ` Run: /wogi-test-browser ${browserSuggestion.flows[0]}`));
457
+ }
458
+ } catch (err) {
459
+ if (process.env.DEBUG) console.error(`[DEBUG] Browser test suggestion: ${err.message}`);
460
+ }
461
+ }
462
+
463
+ // v2.0: Refresh component index after task if configured
464
+ const scanOn = config.componentIndex?.scanOn || [];
465
+ if (config.componentIndex?.autoScan !== false && scanOn.includes('afterTask')) {
466
+ try {
467
+ console.log(color('dim', '🔄 Refreshing component index...'));
468
+ execFileSync('bash', ['scripts/flow-map-index', 'scan', '--quiet'], {
469
+ encoding: 'utf-8',
470
+ stdio: 'pipe'
471
+ });
472
+ if (process.env.DEBUG) {
473
+ console.log(color('dim', ' Component index updated'));
474
+ }
475
+ } catch (err) {
476
+ if (process.env.DEBUG) console.error(`[DEBUG] Component index refresh: ${err.message}`);
477
+ }
478
+ }
479
+
480
+ // v1.7.0: Check context health after task
481
+ if (config.contextMonitor?.checkAfterTask !== false) {
482
+ warnIfContextHigh();
483
+ }
484
+ }
485
+
486
+ main().catch(err => {
487
+ console.error(`Error: ${err.message}`);
488
+ process.exit(1);
489
+ });