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,348 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Wogi Flow - Component Index Scanner
|
|
4
|
+
# Auto-generates component index from codebase
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
10
|
+
WORKFLOW_DIR="$PROJECT_ROOT/.workflow"
|
|
11
|
+
INDEX_FILE="$WORKFLOW_DIR/state/component-index.json"
|
|
12
|
+
CONFIG_FILE="$WORKFLOW_DIR/config.json"
|
|
13
|
+
|
|
14
|
+
# Colors
|
|
15
|
+
GREEN='\033[0;32m'
|
|
16
|
+
YELLOW='\033[1;33m'
|
|
17
|
+
CYAN='\033[0;36m'
|
|
18
|
+
RED='\033[0;31m'
|
|
19
|
+
BOLD='\033[1m'
|
|
20
|
+
DIM='\033[2m'
|
|
21
|
+
NC='\033[0m'
|
|
22
|
+
|
|
23
|
+
show_help() {
|
|
24
|
+
echo "Wogi Flow - Component Index"
|
|
25
|
+
echo ""
|
|
26
|
+
echo "Usage: flow map-index [command]"
|
|
27
|
+
echo ""
|
|
28
|
+
echo "Commands:"
|
|
29
|
+
echo " (none) Show index summary"
|
|
30
|
+
echo " scan Rescan and regenerate index"
|
|
31
|
+
echo " full Show full index details"
|
|
32
|
+
echo " json Output raw JSON"
|
|
33
|
+
echo ""
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get_config_value() {
|
|
37
|
+
local key="$1"
|
|
38
|
+
local default="$2"
|
|
39
|
+
python3 -c "
|
|
40
|
+
import json
|
|
41
|
+
try:
|
|
42
|
+
with open('$CONFIG_FILE', 'r') as f:
|
|
43
|
+
config = json.load(f)
|
|
44
|
+
keys = '$key'.split('.')
|
|
45
|
+
val = config
|
|
46
|
+
for k in keys:
|
|
47
|
+
val = val.get(k, {})
|
|
48
|
+
print(val if val else '$default')
|
|
49
|
+
except:
|
|
50
|
+
print('$default')
|
|
51
|
+
" 2>/dev/null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
scan_codebase() {
|
|
55
|
+
echo -e "${CYAN}Scanning codebase...${NC}"
|
|
56
|
+
|
|
57
|
+
# Get directories from config
|
|
58
|
+
local dirs=$(python3 -c "
|
|
59
|
+
import json
|
|
60
|
+
try:
|
|
61
|
+
with open('$CONFIG_FILE', 'r') as f:
|
|
62
|
+
config = json.load(f)
|
|
63
|
+
dirs = config.get('componentIndex', {}).get('directories', ['src/components', 'src/hooks', 'src/services'])
|
|
64
|
+
print(' '.join(dirs))
|
|
65
|
+
except:
|
|
66
|
+
print('src/components src/hooks src/services')
|
|
67
|
+
" 2>/dev/null)
|
|
68
|
+
|
|
69
|
+
local ignore_patterns=$(python3 -c "
|
|
70
|
+
import json
|
|
71
|
+
try:
|
|
72
|
+
with open('$CONFIG_FILE', 'r') as f:
|
|
73
|
+
config = json.load(f)
|
|
74
|
+
patterns = config.get('componentIndex', {}).get('ignore', ['*.test.*', '*.spec.*'])
|
|
75
|
+
print(' '.join(patterns))
|
|
76
|
+
except:
|
|
77
|
+
print('*.test.* *.spec.* *.stories.*')
|
|
78
|
+
" 2>/dev/null)
|
|
79
|
+
|
|
80
|
+
# Build find command excludes
|
|
81
|
+
local excludes=""
|
|
82
|
+
for pattern in $ignore_patterns; do
|
|
83
|
+
excludes="$excludes -not -name '$pattern'"
|
|
84
|
+
done
|
|
85
|
+
|
|
86
|
+
# Create Python script to scan and categorize
|
|
87
|
+
python3 << EOF
|
|
88
|
+
import os
|
|
89
|
+
import json
|
|
90
|
+
import re
|
|
91
|
+
from datetime import datetime
|
|
92
|
+
from pathlib import Path
|
|
93
|
+
|
|
94
|
+
PROJECT_ROOT = "$PROJECT_ROOT"
|
|
95
|
+
INDEX_FILE = "$INDEX_FILE"
|
|
96
|
+
DIRS = "$dirs".split()
|
|
97
|
+
IGNORE = "$ignore_patterns".split()
|
|
98
|
+
|
|
99
|
+
def should_ignore(filepath):
|
|
100
|
+
name = os.path.basename(filepath)
|
|
101
|
+
for pattern in IGNORE:
|
|
102
|
+
pattern = pattern.replace('*', '.*')
|
|
103
|
+
if re.match(pattern, name):
|
|
104
|
+
return True
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
def get_exports(filepath):
|
|
108
|
+
"""Extract export names from file"""
|
|
109
|
+
exports = []
|
|
110
|
+
try:
|
|
111
|
+
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
|
|
112
|
+
content = f.read()
|
|
113
|
+
|
|
114
|
+
# Match various export patterns
|
|
115
|
+
patterns = [
|
|
116
|
+
r'export\s+(?:default\s+)?(?:function|class|const|let|var)\s+(\w+)',
|
|
117
|
+
r'export\s+{\s*([^}]+)\s*}',
|
|
118
|
+
r'export\s+default\s+(\w+)',
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
for pattern in patterns:
|
|
122
|
+
matches = re.findall(pattern, content)
|
|
123
|
+
for match in matches:
|
|
124
|
+
if isinstance(match, str):
|
|
125
|
+
# Handle export { A, B, C }
|
|
126
|
+
for name in match.split(','):
|
|
127
|
+
name = name.strip().split(' as ')[0].strip()
|
|
128
|
+
if name and name not in exports:
|
|
129
|
+
exports.append(name)
|
|
130
|
+
except:
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
return exports[:5] # Limit to 5 exports
|
|
134
|
+
|
|
135
|
+
def categorize(filepath, name):
|
|
136
|
+
"""Determine category based on path and name"""
|
|
137
|
+
path_lower = filepath.lower()
|
|
138
|
+
name_lower = name.lower()
|
|
139
|
+
|
|
140
|
+
if '/components/' in path_lower or name_lower.endswith('.component'):
|
|
141
|
+
return 'components'
|
|
142
|
+
elif '/pages/' in path_lower or '/app/' in path_lower and '/api/' not in path_lower:
|
|
143
|
+
return 'pages'
|
|
144
|
+
elif name_lower.startswith('use') or '/hooks/' in path_lower:
|
|
145
|
+
return 'hooks'
|
|
146
|
+
elif '.service' in name_lower or '/services/' in path_lower:
|
|
147
|
+
return 'services'
|
|
148
|
+
elif '.module' in name_lower or '/modules/' in path_lower:
|
|
149
|
+
return 'modules'
|
|
150
|
+
elif '/utils/' in path_lower or '/lib/' in path_lower or '/helpers/' in path_lower:
|
|
151
|
+
return 'utils'
|
|
152
|
+
elif '/api/' in path_lower:
|
|
153
|
+
return 'api'
|
|
154
|
+
else:
|
|
155
|
+
return 'other'
|
|
156
|
+
|
|
157
|
+
def scan():
|
|
158
|
+
index = {
|
|
159
|
+
'lastScan': datetime.now().isoformat(),
|
|
160
|
+
'scanConfig': {
|
|
161
|
+
'directories': DIRS,
|
|
162
|
+
'ignore': IGNORE
|
|
163
|
+
},
|
|
164
|
+
'components': [],
|
|
165
|
+
'pages': [],
|
|
166
|
+
'hooks': [],
|
|
167
|
+
'services': [],
|
|
168
|
+
'modules': [],
|
|
169
|
+
'utils': [],
|
|
170
|
+
'api': [],
|
|
171
|
+
'other': []
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
extensions = {'.tsx', '.jsx', '.ts', '.js', '.vue'}
|
|
175
|
+
|
|
176
|
+
for scan_dir in DIRS:
|
|
177
|
+
full_dir = os.path.join(PROJECT_ROOT, scan_dir)
|
|
178
|
+
if not os.path.exists(full_dir):
|
|
179
|
+
continue
|
|
180
|
+
|
|
181
|
+
for root, dirs, files in os.walk(full_dir):
|
|
182
|
+
# Skip node_modules, __tests__, etc
|
|
183
|
+
dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ['node_modules', '__tests__', '__mocks__', 'dist', 'build']]
|
|
184
|
+
|
|
185
|
+
for file in files:
|
|
186
|
+
filepath = os.path.join(root, file)
|
|
187
|
+
rel_path = os.path.relpath(filepath, PROJECT_ROOT)
|
|
188
|
+
|
|
189
|
+
# Check extension
|
|
190
|
+
ext = os.path.splitext(file)[1]
|
|
191
|
+
if ext not in extensions:
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
# Check ignore patterns
|
|
195
|
+
if should_ignore(filepath):
|
|
196
|
+
continue
|
|
197
|
+
|
|
198
|
+
# Get name without extension
|
|
199
|
+
name = os.path.splitext(file)[0]
|
|
200
|
+
if name in ['index', 'types', 'constants', 'styles']:
|
|
201
|
+
# Use parent directory name
|
|
202
|
+
name = os.path.basename(os.path.dirname(filepath))
|
|
203
|
+
|
|
204
|
+
# Categorize
|
|
205
|
+
category = categorize(rel_path, name)
|
|
206
|
+
|
|
207
|
+
# Get exports
|
|
208
|
+
exports = get_exports(filepath)
|
|
209
|
+
|
|
210
|
+
entry = {
|
|
211
|
+
'name': name,
|
|
212
|
+
'path': rel_path,
|
|
213
|
+
'exports': exports
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
index[category].append(entry)
|
|
217
|
+
|
|
218
|
+
# Sort each category
|
|
219
|
+
for cat in ['components', 'pages', 'hooks', 'services', 'modules', 'utils', 'api', 'other']:
|
|
220
|
+
index[cat] = sorted(index[cat], key=lambda x: x['name'].lower())
|
|
221
|
+
|
|
222
|
+
# Remove empty categories except core ones
|
|
223
|
+
if not index['other']:
|
|
224
|
+
del index['other']
|
|
225
|
+
if not index['api']:
|
|
226
|
+
del index['api']
|
|
227
|
+
|
|
228
|
+
# Save
|
|
229
|
+
with open(INDEX_FILE, 'w') as f:
|
|
230
|
+
json.dump(index, f, indent=2)
|
|
231
|
+
|
|
232
|
+
# Print summary
|
|
233
|
+
total = sum(len(index.get(cat, [])) for cat in ['components', 'pages', 'hooks', 'services', 'modules', 'utils'])
|
|
234
|
+
print(f"Scanned {len(DIRS)} directories")
|
|
235
|
+
print(f"Found {total} items")
|
|
236
|
+
|
|
237
|
+
return index
|
|
238
|
+
|
|
239
|
+
scan()
|
|
240
|
+
EOF
|
|
241
|
+
|
|
242
|
+
echo -e "${GREEN}ā${NC} Index updated"
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
show_summary() {
|
|
246
|
+
if [ ! -f "$INDEX_FILE" ]; then
|
|
247
|
+
echo -e "${YELLOW}No index found. Run: flow map-index scan${NC}"
|
|
248
|
+
exit 1
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
python3 << EOF
|
|
252
|
+
import json
|
|
253
|
+
from datetime import datetime
|
|
254
|
+
|
|
255
|
+
with open('$INDEX_FILE', 'r') as f:
|
|
256
|
+
index = json.load(f)
|
|
257
|
+
|
|
258
|
+
last_scan = index.get('lastScan', 'Never')
|
|
259
|
+
if last_scan != 'Never':
|
|
260
|
+
try:
|
|
261
|
+
dt = datetime.fromisoformat(last_scan.replace('Z', '+00:00'))
|
|
262
|
+
last_scan = dt.strftime('%Y-%m-%d %H:%M')
|
|
263
|
+
except:
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
print("\nš¦ Component Index")
|
|
267
|
+
print(f"\nLast scan: {last_scan}")
|
|
268
|
+
print()
|
|
269
|
+
print("| Category | Count |")
|
|
270
|
+
print("|------------|-------|")
|
|
271
|
+
|
|
272
|
+
total = 0
|
|
273
|
+
for cat in ['components', 'pages', 'hooks', 'services', 'modules', 'utils']:
|
|
274
|
+
count = len(index.get(cat, []))
|
|
275
|
+
total += count
|
|
276
|
+
if count > 0:
|
|
277
|
+
print(f"| {cat.capitalize():<10} | {count:>5} |")
|
|
278
|
+
|
|
279
|
+
print(f"\nTotal: {total} items")
|
|
280
|
+
print("\nRun: flow map-index full (for details)")
|
|
281
|
+
print("Run: flow map-index scan (to refresh)")
|
|
282
|
+
EOF
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
show_full() {
|
|
286
|
+
if [ ! -f "$INDEX_FILE" ]; then
|
|
287
|
+
echo -e "${YELLOW}No index found. Run: flow map-index scan${NC}"
|
|
288
|
+
exit 1
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
python3 << EOF
|
|
292
|
+
import json
|
|
293
|
+
|
|
294
|
+
with open('$INDEX_FILE', 'r') as f:
|
|
295
|
+
index = json.load(f)
|
|
296
|
+
|
|
297
|
+
print("\nš¦ Component Index (Full)\n")
|
|
298
|
+
|
|
299
|
+
for cat in ['components', 'pages', 'hooks', 'services', 'modules', 'utils']:
|
|
300
|
+
items = index.get(cat, [])
|
|
301
|
+
if not items:
|
|
302
|
+
continue
|
|
303
|
+
|
|
304
|
+
print(f"## {cat.capitalize()} ({len(items)})\n")
|
|
305
|
+
print("| Name | Path | Exports |")
|
|
306
|
+
print("|------|------|---------|")
|
|
307
|
+
|
|
308
|
+
for item in items[:50]: # Limit display
|
|
309
|
+
name = item['name']
|
|
310
|
+
path = item['path']
|
|
311
|
+
exports = ', '.join(item.get('exports', [])[:3])
|
|
312
|
+
if len(item.get('exports', [])) > 3:
|
|
313
|
+
exports += '...'
|
|
314
|
+
print(f"| {name} | {path} | {exports} |")
|
|
315
|
+
|
|
316
|
+
if len(items) > 50:
|
|
317
|
+
print(f"\n... and {len(items) - 50} more\n")
|
|
318
|
+
print()
|
|
319
|
+
EOF
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
show_json() {
|
|
323
|
+
if [ ! -f "$INDEX_FILE" ]; then
|
|
324
|
+
echo "{}"
|
|
325
|
+
exit 1
|
|
326
|
+
fi
|
|
327
|
+
cat "$INDEX_FILE"
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
# Main
|
|
331
|
+
case "${1:-}" in
|
|
332
|
+
scan)
|
|
333
|
+
scan_codebase
|
|
334
|
+
show_summary
|
|
335
|
+
;;
|
|
336
|
+
full)
|
|
337
|
+
show_full
|
|
338
|
+
;;
|
|
339
|
+
json)
|
|
340
|
+
show_json
|
|
341
|
+
;;
|
|
342
|
+
help|--help|-h)
|
|
343
|
+
show_help
|
|
344
|
+
;;
|
|
345
|
+
*)
|
|
346
|
+
show_summary
|
|
347
|
+
;;
|
|
348
|
+
esac
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Wogi Flow - App Map Sync
|
|
4
|
+
# Compare auto-generated index with curated app-map
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
10
|
+
WORKFLOW_DIR="$PROJECT_ROOT/.workflow"
|
|
11
|
+
INDEX_FILE="$WORKFLOW_DIR/state/component-index.json"
|
|
12
|
+
APPMAP_FILE="$WORKFLOW_DIR/state/app-map.md"
|
|
13
|
+
|
|
14
|
+
# Colors
|
|
15
|
+
GREEN='\033[0;32m'
|
|
16
|
+
YELLOW='\033[1;33m'
|
|
17
|
+
CYAN='\033[0;36m'
|
|
18
|
+
RED='\033[0;31m'
|
|
19
|
+
BOLD='\033[1m'
|
|
20
|
+
DIM='\033[2m'
|
|
21
|
+
NC='\033[0m'
|
|
22
|
+
|
|
23
|
+
show_help() {
|
|
24
|
+
echo "Wogi Flow - App Map Sync"
|
|
25
|
+
echo ""
|
|
26
|
+
echo "Compare auto-generated index with curated app-map"
|
|
27
|
+
echo ""
|
|
28
|
+
echo "Usage: flow map-sync"
|
|
29
|
+
echo ""
|
|
30
|
+
echo "This will:"
|
|
31
|
+
echo " 1. Compare component-index.json with app-map.md"
|
|
32
|
+
echo " 2. Show items in codebase but not in app-map"
|
|
33
|
+
echo " 3. Show items in app-map but not in codebase (stale)"
|
|
34
|
+
echo " 4. Offer to update app-map"
|
|
35
|
+
echo ""
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
check_files() {
|
|
39
|
+
if [ ! -f "$INDEX_FILE" ]; then
|
|
40
|
+
echo -e "${YELLOW}No component index found.${NC}"
|
|
41
|
+
echo "Running scan first..."
|
|
42
|
+
"$SCRIPT_DIR/flow-map-index" scan
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
if [ ! -f "$APPMAP_FILE" ]; then
|
|
46
|
+
echo -e "${RED}No app-map.md found at $APPMAP_FILE${NC}"
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
sync_maps() {
|
|
52
|
+
python3 << 'EOF'
|
|
53
|
+
import json
|
|
54
|
+
import re
|
|
55
|
+
import os
|
|
56
|
+
from datetime import datetime
|
|
57
|
+
|
|
58
|
+
INDEX_FILE = os.environ.get('INDEX_FILE', '.workflow/state/component-index.json')
|
|
59
|
+
APPMAP_FILE = os.environ.get('APPMAP_FILE', '.workflow/state/app-map.md')
|
|
60
|
+
|
|
61
|
+
# Load index
|
|
62
|
+
with open(INDEX_FILE, 'r') as f:
|
|
63
|
+
index = json.load(f)
|
|
64
|
+
|
|
65
|
+
# Load app-map and extract component names
|
|
66
|
+
with open(APPMAP_FILE, 'r') as f:
|
|
67
|
+
appmap_content = f.read()
|
|
68
|
+
|
|
69
|
+
# Extract names from app-map tables
|
|
70
|
+
# Pattern matches: | ComponentName | ... |
|
|
71
|
+
appmap_names = set()
|
|
72
|
+
appmap_paths = {}
|
|
73
|
+
|
|
74
|
+
table_pattern = r'\|\s*([A-Za-z][A-Za-z0-9_-]*)\s*\|'
|
|
75
|
+
path_pattern = r'\|\s*([A-Za-z][A-Za-z0-9_-]*)\s*\|[^|]*\|[^|]*\|\s*([^\s|]+)\s*\|'
|
|
76
|
+
|
|
77
|
+
for match in re.finditer(table_pattern, appmap_content):
|
|
78
|
+
name = match.group(1)
|
|
79
|
+
# Skip table headers
|
|
80
|
+
if name.lower() not in ['component', 'screen', 'name', 'hook', 'service', 'module', 'route', 'path', 'description', 'variants', 'type']:
|
|
81
|
+
appmap_names.add(name.lower())
|
|
82
|
+
|
|
83
|
+
# Also try to extract paths for stale detection
|
|
84
|
+
for match in re.finditer(path_pattern, appmap_content):
|
|
85
|
+
name = match.group(1).lower()
|
|
86
|
+
path = match.group(2)
|
|
87
|
+
if name not in ['component', 'screen', 'name', 'path']:
|
|
88
|
+
appmap_paths[name] = path
|
|
89
|
+
|
|
90
|
+
# Get all names from index
|
|
91
|
+
index_names = {}
|
|
92
|
+
for category in ['components', 'pages', 'hooks', 'services', 'modules', 'utils']:
|
|
93
|
+
for item in index.get(category, []):
|
|
94
|
+
name_lower = item['name'].lower()
|
|
95
|
+
index_names[name_lower] = {
|
|
96
|
+
'name': item['name'],
|
|
97
|
+
'path': item['path'],
|
|
98
|
+
'category': category
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# Find differences
|
|
102
|
+
in_code_not_map = []
|
|
103
|
+
in_map_not_code = []
|
|
104
|
+
in_sync = []
|
|
105
|
+
|
|
106
|
+
for name_lower, info in index_names.items():
|
|
107
|
+
if name_lower in appmap_names:
|
|
108
|
+
in_sync.append(info['name'])
|
|
109
|
+
else:
|
|
110
|
+
in_code_not_map.append(info)
|
|
111
|
+
|
|
112
|
+
for name_lower in appmap_names:
|
|
113
|
+
if name_lower not in index_names:
|
|
114
|
+
path = appmap_paths.get(name_lower, 'unknown path')
|
|
115
|
+
in_map_not_code.append({'name': name_lower, 'path': path})
|
|
116
|
+
|
|
117
|
+
# Output
|
|
118
|
+
last_scan = index.get('lastScan', 'unknown')
|
|
119
|
+
try:
|
|
120
|
+
dt = datetime.fromisoformat(last_scan.replace('Z', '+00:00'))
|
|
121
|
+
last_scan = dt.strftime('%Y-%m-%d')
|
|
122
|
+
except:
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
print("\nš App Map Sync")
|
|
126
|
+
print()
|
|
127
|
+
print(f"Comparing component-index.json (scanned: {last_scan})")
|
|
128
|
+
print(f" with app-map.md")
|
|
129
|
+
print()
|
|
130
|
+
print("ā" * 55)
|
|
131
|
+
|
|
132
|
+
if in_code_not_map:
|
|
133
|
+
print()
|
|
134
|
+
print("š„ IN CODEBASE, NOT IN APP-MAP (consider adding):")
|
|
135
|
+
print()
|
|
136
|
+
|
|
137
|
+
# Group by category
|
|
138
|
+
by_category = {}
|
|
139
|
+
for item in in_code_not_map:
|
|
140
|
+
cat = item['category']
|
|
141
|
+
if cat not in by_category:
|
|
142
|
+
by_category[cat] = []
|
|
143
|
+
by_category[cat].append(item)
|
|
144
|
+
|
|
145
|
+
for cat, items in sorted(by_category.items()):
|
|
146
|
+
print(f" {cat.capitalize()}:")
|
|
147
|
+
for item in items[:10]:
|
|
148
|
+
print(f" ⢠{item['name']} ({item['path']})")
|
|
149
|
+
if len(items) > 10:
|
|
150
|
+
print(f" ... and {len(items) - 10} more")
|
|
151
|
+
print()
|
|
152
|
+
|
|
153
|
+
print("ā" * 55)
|
|
154
|
+
|
|
155
|
+
if in_map_not_code:
|
|
156
|
+
print()
|
|
157
|
+
print("š¤ IN APP-MAP, NOT IN CODEBASE (possibly stale):")
|
|
158
|
+
print()
|
|
159
|
+
for item in in_map_not_code[:10]:
|
|
160
|
+
print(f" ⢠{item['name']} (was at {item['path']})")
|
|
161
|
+
if len(in_map_not_code) > 10:
|
|
162
|
+
print(f" ... and {len(in_map_not_code) - 10} more")
|
|
163
|
+
print()
|
|
164
|
+
print("ā" * 55)
|
|
165
|
+
|
|
166
|
+
print()
|
|
167
|
+
print(f"ā
IN SYNC: {len(in_sync)} items match")
|
|
168
|
+
print()
|
|
169
|
+
print("ā" * 55)
|
|
170
|
+
print()
|
|
171
|
+
|
|
172
|
+
# Summary
|
|
173
|
+
missing_count = len(in_code_not_map)
|
|
174
|
+
stale_count = len(in_map_not_code)
|
|
175
|
+
|
|
176
|
+
if missing_count == 0 and stale_count == 0:
|
|
177
|
+
print("š App map is fully in sync with codebase!")
|
|
178
|
+
else:
|
|
179
|
+
print("Suggestions:")
|
|
180
|
+
if missing_count > 0:
|
|
181
|
+
print(f" ⢠Add {missing_count} missing items: /wogi-map-add")
|
|
182
|
+
if stale_count > 0:
|
|
183
|
+
print(f" ⢠Review {stale_count} possibly stale entries")
|
|
184
|
+
print()
|
|
185
|
+
EOF
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# Export paths for Python
|
|
189
|
+
export INDEX_FILE
|
|
190
|
+
export APPMAP_FILE
|
|
191
|
+
|
|
192
|
+
# Main
|
|
193
|
+
case "${1:-}" in
|
|
194
|
+
help|--help|-h)
|
|
195
|
+
show_help
|
|
196
|
+
;;
|
|
197
|
+
*)
|
|
198
|
+
check_files
|
|
199
|
+
sync_maps
|
|
200
|
+
;;
|
|
201
|
+
esac
|