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,556 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - CLI Hooks Manager
|
|
5
|
+
*
|
|
6
|
+
* Multi-CLI hook manager supporting Claude Code, Gemini, Codex, etc.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* flow hooks setup # Install hooks for configured targets
|
|
10
|
+
* flow hooks setup --target claude-code # Install for specific CLI
|
|
11
|
+
* flow hooks remove # Remove all hooks
|
|
12
|
+
* flow hooks remove --target claude-code
|
|
13
|
+
* flow hooks status # Show hook status
|
|
14
|
+
* flow hooks test <hook> # Test a hook with sample input
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const {
|
|
20
|
+
getProjectRoot,
|
|
21
|
+
getConfig,
|
|
22
|
+
color,
|
|
23
|
+
success,
|
|
24
|
+
warn,
|
|
25
|
+
error
|
|
26
|
+
} = require('./flow-utils');
|
|
27
|
+
|
|
28
|
+
const { getAdapter, getAllAdapters, getAvailableAdapters } = require('./hooks/adapters');
|
|
29
|
+
|
|
30
|
+
const PROJECT_ROOT = getProjectRoot();
|
|
31
|
+
const HOOK_MARKER = '// WOGI_FLOW_MANAGED_HOOKS';
|
|
32
|
+
|
|
33
|
+
// ============================================================
|
|
34
|
+
// Configuration
|
|
35
|
+
// ============================================================
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get hooks configuration
|
|
39
|
+
*/
|
|
40
|
+
function getHooksConfig() {
|
|
41
|
+
const config = getConfig();
|
|
42
|
+
return config.hooks || {
|
|
43
|
+
enabled: true,
|
|
44
|
+
targets: ['claude-code'],
|
|
45
|
+
gracefulDegradation: true,
|
|
46
|
+
timeout: 5000,
|
|
47
|
+
rules: {
|
|
48
|
+
taskGating: { enabled: true, blockWithoutTask: true },
|
|
49
|
+
validation: { enabled: true, runAfterEdit: true },
|
|
50
|
+
loopEnforcement: { enabled: true },
|
|
51
|
+
componentReuse: { enabled: true, threshold: 80 },
|
|
52
|
+
sessionContext: { enabled: true, loadSuspendedTasks: true },
|
|
53
|
+
autoLogging: { enabled: true }
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get target CLIs to install hooks for
|
|
60
|
+
*/
|
|
61
|
+
function getTargets(specificTarget = null) {
|
|
62
|
+
if (specificTarget) {
|
|
63
|
+
return [specificTarget];
|
|
64
|
+
}
|
|
65
|
+
const config = getHooksConfig();
|
|
66
|
+
return config.targets || ['claude-code'];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ============================================================
|
|
70
|
+
// Setup / Install
|
|
71
|
+
// ============================================================
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Install hooks for a specific target CLI
|
|
75
|
+
*/
|
|
76
|
+
function installForTarget(targetName) {
|
|
77
|
+
const adapter = getAdapter(targetName);
|
|
78
|
+
if (!adapter) {
|
|
79
|
+
error(`Unknown target: ${targetName}`);
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!adapter.isAvailable()) {
|
|
84
|
+
warn(`${targetName} not detected in project (skipping)`);
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log(` Installing hooks for ${targetName}...`);
|
|
89
|
+
|
|
90
|
+
const config = getHooksConfig();
|
|
91
|
+
const hooksConfig = adapter.generateConfig(config.rules, PROJECT_ROOT);
|
|
92
|
+
|
|
93
|
+
// For Claude Code, we need to merge into settings.local.json
|
|
94
|
+
if (targetName === 'claude-code') {
|
|
95
|
+
return installClaudeCodeHooks(adapter, hooksConfig);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// For other CLIs, implement their specific installation
|
|
99
|
+
warn(` ${targetName} installation not yet implemented`);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Install Claude Code hooks into settings.local.json
|
|
105
|
+
*/
|
|
106
|
+
function installClaudeCodeHooks(adapter, hooksConfig) {
|
|
107
|
+
const configPath = adapter.getLocalConfigPath();
|
|
108
|
+
const configDir = path.dirname(configPath);
|
|
109
|
+
|
|
110
|
+
// Ensure .claude directory exists
|
|
111
|
+
if (!fs.existsSync(configDir)) {
|
|
112
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Read existing config
|
|
116
|
+
let existingConfig = {};
|
|
117
|
+
if (fs.existsSync(configPath)) {
|
|
118
|
+
try {
|
|
119
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
120
|
+
existingConfig = JSON.parse(content);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
warn(` Could not parse existing config, will create new`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Check if we're overwriting non-Wogi hooks
|
|
127
|
+
if (existingConfig.hooks && !existingConfig._wogiFlowManaged) {
|
|
128
|
+
// Backup existing
|
|
129
|
+
const backupPath = configPath + '.backup';
|
|
130
|
+
fs.writeFileSync(backupPath, JSON.stringify(existingConfig, null, 2));
|
|
131
|
+
warn(` Backed up existing hooks to ${path.basename(backupPath)}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Merge hooks
|
|
135
|
+
const newConfig = {
|
|
136
|
+
...existingConfig,
|
|
137
|
+
hooks: hooksConfig.hooks,
|
|
138
|
+
_wogiFlowManaged: true,
|
|
139
|
+
_wogiFlowVersion: '1.0.0'
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Write config
|
|
143
|
+
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2));
|
|
144
|
+
console.log(` ${color('green', '✓')} Hooks written to ${path.relative(PROJECT_ROOT, configPath)}`);
|
|
145
|
+
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Install hooks for all configured targets
|
|
151
|
+
*/
|
|
152
|
+
function setupHooks(options = {}) {
|
|
153
|
+
const { target, force } = options;
|
|
154
|
+
|
|
155
|
+
console.log(color('cyan', '🪝 Setting Up CLI Hooks'));
|
|
156
|
+
console.log('');
|
|
157
|
+
|
|
158
|
+
const config = getHooksConfig();
|
|
159
|
+
|
|
160
|
+
if (!config.enabled) {
|
|
161
|
+
warn('Hooks are disabled in config (hooks.enabled = false)');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const targets = getTargets(target);
|
|
166
|
+
const results = [];
|
|
167
|
+
|
|
168
|
+
for (const t of targets) {
|
|
169
|
+
const success = installForTarget(t);
|
|
170
|
+
results.push({ target: t, success });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
console.log('');
|
|
174
|
+
|
|
175
|
+
const successCount = results.filter(r => r.success).length;
|
|
176
|
+
if (successCount > 0) {
|
|
177
|
+
success(`Installed hooks for ${successCount} CLI${successCount !== 1 ? 's' : ''}`);
|
|
178
|
+
} else {
|
|
179
|
+
warn('No hooks were installed');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Show what was configured
|
|
183
|
+
console.log('');
|
|
184
|
+
console.log(color('dim', 'Configured rules:'));
|
|
185
|
+
for (const [rule, settings] of Object.entries(config.rules || {})) {
|
|
186
|
+
const enabled = settings.enabled !== false;
|
|
187
|
+
const icon = enabled ? color('green', '✓') : color('dim', '○');
|
|
188
|
+
console.log(` ${icon} ${rule}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ============================================================
|
|
193
|
+
// Remove
|
|
194
|
+
// ============================================================
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Remove hooks for a specific target
|
|
198
|
+
*/
|
|
199
|
+
function removeForTarget(targetName) {
|
|
200
|
+
const adapter = getAdapter(targetName);
|
|
201
|
+
if (!adapter) {
|
|
202
|
+
warn(`Unknown target: ${targetName}`);
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
console.log(` Removing hooks for ${targetName}...`);
|
|
207
|
+
|
|
208
|
+
if (targetName === 'claude-code') {
|
|
209
|
+
return removeClaudeCodeHooks(adapter);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
warn(` ${targetName} removal not yet implemented`);
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Remove Claude Code hooks from settings.local.json
|
|
218
|
+
*/
|
|
219
|
+
function removeClaudeCodeHooks(adapter) {
|
|
220
|
+
const configPath = adapter.getLocalConfigPath();
|
|
221
|
+
|
|
222
|
+
if (!fs.existsSync(configPath)) {
|
|
223
|
+
console.log(` ${color('dim', '-')} No config file found`);
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
229
|
+
const config = JSON.parse(content);
|
|
230
|
+
|
|
231
|
+
if (!config._wogiFlowManaged) {
|
|
232
|
+
warn(` Config not managed by Wogi Flow (skipping)`);
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Remove hooks and our marker
|
|
237
|
+
delete config.hooks;
|
|
238
|
+
delete config._wogiFlowManaged;
|
|
239
|
+
delete config._wogiFlowVersion;
|
|
240
|
+
|
|
241
|
+
// Check if config is now empty
|
|
242
|
+
if (Object.keys(config).length === 0) {
|
|
243
|
+
fs.unlinkSync(configPath);
|
|
244
|
+
console.log(` ${color('green', '✓')} Removed ${path.basename(configPath)}`);
|
|
245
|
+
} else {
|
|
246
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
247
|
+
console.log(` ${color('green', '✓')} Removed hooks from ${path.basename(configPath)}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Restore backup if exists
|
|
251
|
+
const backupPath = configPath + '.backup';
|
|
252
|
+
if (fs.existsSync(backupPath)) {
|
|
253
|
+
const backupContent = fs.readFileSync(backupPath, 'utf-8');
|
|
254
|
+
const backupConfig = JSON.parse(backupContent);
|
|
255
|
+
if (backupConfig.hooks) {
|
|
256
|
+
const finalConfig = { ...config, hooks: backupConfig.hooks };
|
|
257
|
+
fs.writeFileSync(configPath, JSON.stringify(finalConfig, null, 2));
|
|
258
|
+
fs.unlinkSync(backupPath);
|
|
259
|
+
console.log(` ${color('green', '✓')} Restored original hooks from backup`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return true;
|
|
264
|
+
} catch (err) {
|
|
265
|
+
error(` Failed to remove: ${err.message}`);
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Remove all hooks
|
|
272
|
+
*/
|
|
273
|
+
function removeHooks(options = {}) {
|
|
274
|
+
const { target } = options;
|
|
275
|
+
|
|
276
|
+
console.log(color('cyan', '🪝 Removing CLI Hooks'));
|
|
277
|
+
console.log('');
|
|
278
|
+
|
|
279
|
+
const targets = getTargets(target);
|
|
280
|
+
const results = [];
|
|
281
|
+
|
|
282
|
+
for (const t of targets) {
|
|
283
|
+
const success = removeForTarget(t);
|
|
284
|
+
results.push({ target: t, success });
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
console.log('');
|
|
288
|
+
|
|
289
|
+
const successCount = results.filter(r => r.success).length;
|
|
290
|
+
if (successCount > 0) {
|
|
291
|
+
success(`Removed hooks from ${successCount} CLI${successCount !== 1 ? 's' : ''}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ============================================================
|
|
296
|
+
// Status
|
|
297
|
+
// ============================================================
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Show hook status
|
|
301
|
+
*/
|
|
302
|
+
function showStatus() {
|
|
303
|
+
console.log(color('cyan', '🪝 CLI Hooks Status'));
|
|
304
|
+
console.log('');
|
|
305
|
+
|
|
306
|
+
const config = getHooksConfig();
|
|
307
|
+
|
|
308
|
+
// Overall status
|
|
309
|
+
console.log(`Hooks enabled: ${config.enabled !== false ? color('green', 'Yes') : color('red', 'No')}`);
|
|
310
|
+
console.log(`Configured targets: ${(config.targets || ['claude-code']).join(', ')}`);
|
|
311
|
+
console.log('');
|
|
312
|
+
|
|
313
|
+
// Per-target status
|
|
314
|
+
console.log('Target Status:');
|
|
315
|
+
const allAdapters = getAllAdapters();
|
|
316
|
+
|
|
317
|
+
for (const [name, adapter] of Object.entries(allAdapters)) {
|
|
318
|
+
const available = adapter.isAvailable();
|
|
319
|
+
const installed = checkIfInstalled(adapter);
|
|
320
|
+
|
|
321
|
+
let status;
|
|
322
|
+
if (!available) {
|
|
323
|
+
status = color('dim', 'Not detected');
|
|
324
|
+
} else if (installed) {
|
|
325
|
+
status = color('green', 'Installed');
|
|
326
|
+
} else {
|
|
327
|
+
status = color('yellow', 'Available (not installed)');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
console.log(` ${name}: ${status}`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
console.log('');
|
|
334
|
+
|
|
335
|
+
// Rule status
|
|
336
|
+
console.log('Rules:');
|
|
337
|
+
for (const [rule, settings] of Object.entries(config.rules || {})) {
|
|
338
|
+
const enabled = settings?.enabled !== false;
|
|
339
|
+
const icon = enabled ? color('green', '✓') : color('dim', '○');
|
|
340
|
+
console.log(` ${icon} ${rule}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Check if hooks are installed for an adapter
|
|
346
|
+
*/
|
|
347
|
+
function checkIfInstalled(adapter) {
|
|
348
|
+
if (adapter.name === 'claude-code') {
|
|
349
|
+
const configPath = adapter.getLocalConfigPath();
|
|
350
|
+
if (!fs.existsSync(configPath)) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
try {
|
|
354
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
355
|
+
const config = JSON.parse(content);
|
|
356
|
+
return config._wogiFlowManaged === true;
|
|
357
|
+
} catch {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// ============================================================
|
|
365
|
+
// Test
|
|
366
|
+
// ============================================================
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Test a hook with sample input
|
|
370
|
+
*/
|
|
371
|
+
async function testHook(hookName) {
|
|
372
|
+
console.log(color('cyan', `🧪 Testing Hook: ${hookName}`));
|
|
373
|
+
console.log('');
|
|
374
|
+
|
|
375
|
+
const testInputs = {
|
|
376
|
+
'session-start': { hook_event_name: 'SessionStart', source: 'startup' },
|
|
377
|
+
'pre-tool-use': {
|
|
378
|
+
hook_event_name: 'PreToolUse',
|
|
379
|
+
tool_name: 'Edit',
|
|
380
|
+
tool_input: { file_path: 'src/test.ts' }
|
|
381
|
+
},
|
|
382
|
+
'post-tool-use': {
|
|
383
|
+
hook_event_name: 'PostToolUse',
|
|
384
|
+
tool_name: 'Edit',
|
|
385
|
+
tool_input: { file_path: 'src/test.ts' },
|
|
386
|
+
tool_response: { success: true }
|
|
387
|
+
},
|
|
388
|
+
'stop': { hook_event_name: 'Stop' },
|
|
389
|
+
'session-end': { hook_event_name: 'SessionEnd', reason: 'manual' }
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
const input = testInputs[hookName];
|
|
393
|
+
if (!input) {
|
|
394
|
+
error(`Unknown hook: ${hookName}`);
|
|
395
|
+
console.log('Available hooks: ' + Object.keys(testInputs).join(', '));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
console.log('Input:', JSON.stringify(input, null, 2));
|
|
400
|
+
console.log('');
|
|
401
|
+
|
|
402
|
+
// Run the hook
|
|
403
|
+
const hookPath = path.join(__dirname, 'hooks', 'entry', 'claude-code', `${hookName}.js`);
|
|
404
|
+
if (!fs.existsSync(hookPath)) {
|
|
405
|
+
error(`Hook script not found: ${hookPath}`);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const { spawn } = require('child_process');
|
|
410
|
+
const proc = spawn('node', [hookPath], {
|
|
411
|
+
cwd: PROJECT_ROOT,
|
|
412
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
proc.stdin.write(JSON.stringify(input));
|
|
416
|
+
proc.stdin.end();
|
|
417
|
+
|
|
418
|
+
let stdout = '';
|
|
419
|
+
let stderr = '';
|
|
420
|
+
|
|
421
|
+
proc.stdout.on('data', (data) => { stdout += data; });
|
|
422
|
+
proc.stderr.on('data', (data) => { stderr += data; });
|
|
423
|
+
|
|
424
|
+
proc.on('close', (code) => {
|
|
425
|
+
console.log(`Exit code: ${code}`);
|
|
426
|
+
if (stderr) {
|
|
427
|
+
console.log('Stderr:', stderr);
|
|
428
|
+
}
|
|
429
|
+
if (stdout) {
|
|
430
|
+
console.log('Output:');
|
|
431
|
+
try {
|
|
432
|
+
const output = JSON.parse(stdout);
|
|
433
|
+
console.log(JSON.stringify(output, null, 2));
|
|
434
|
+
} catch {
|
|
435
|
+
console.log(stdout);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ============================================================
|
|
442
|
+
// Help
|
|
443
|
+
// ============================================================
|
|
444
|
+
|
|
445
|
+
function showHelp() {
|
|
446
|
+
console.log(`
|
|
447
|
+
Wogi Flow - CLI Hooks Manager
|
|
448
|
+
|
|
449
|
+
Manage hooks for AI CLI tools (Claude Code, Gemini, Codex, etc.)
|
|
450
|
+
|
|
451
|
+
Usage:
|
|
452
|
+
flow hooks setup Install hooks for configured targets
|
|
453
|
+
flow hooks setup --target X Install for specific CLI
|
|
454
|
+
flow hooks remove Remove all hooks
|
|
455
|
+
flow hooks remove --target X Remove for specific CLI
|
|
456
|
+
flow hooks status Show hook status
|
|
457
|
+
flow hooks test <hook> Test a hook
|
|
458
|
+
|
|
459
|
+
Available targets:
|
|
460
|
+
claude-code Claude Code CLI (primary)
|
|
461
|
+
gemini Gemini CLI (future)
|
|
462
|
+
codex Codex CLI (future)
|
|
463
|
+
|
|
464
|
+
Configuration:
|
|
465
|
+
Configure hooks in .workflow/config.json under "hooks":
|
|
466
|
+
|
|
467
|
+
{
|
|
468
|
+
"hooks": {
|
|
469
|
+
"enabled": true,
|
|
470
|
+
"targets": ["claude-code"],
|
|
471
|
+
"rules": {
|
|
472
|
+
"taskGating": { "enabled": true },
|
|
473
|
+
"validation": { "enabled": true },
|
|
474
|
+
"loopEnforcement": { "enabled": true },
|
|
475
|
+
"componentReuse": { "enabled": true },
|
|
476
|
+
"sessionContext": { "enabled": true },
|
|
477
|
+
"autoLogging": { "enabled": true }
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
Test hooks:
|
|
483
|
+
flow hooks test session-start
|
|
484
|
+
flow hooks test pre-tool-use
|
|
485
|
+
flow hooks test post-tool-use
|
|
486
|
+
flow hooks test stop
|
|
487
|
+
flow hooks test session-end
|
|
488
|
+
`);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// ============================================================
|
|
492
|
+
// CLI
|
|
493
|
+
// ============================================================
|
|
494
|
+
|
|
495
|
+
function main() {
|
|
496
|
+
const args = process.argv.slice(2);
|
|
497
|
+
const command = args[0];
|
|
498
|
+
|
|
499
|
+
// Parse options
|
|
500
|
+
const options = {};
|
|
501
|
+
for (let i = 1; i < args.length; i++) {
|
|
502
|
+
if (args[i] === '--target' && args[i + 1]) {
|
|
503
|
+
options.target = args[i + 1];
|
|
504
|
+
i++;
|
|
505
|
+
} else if (args[i] === '--force') {
|
|
506
|
+
options.force = true;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
switch (command) {
|
|
511
|
+
case 'setup':
|
|
512
|
+
case 'install':
|
|
513
|
+
setupHooks(options);
|
|
514
|
+
break;
|
|
515
|
+
case 'remove':
|
|
516
|
+
case 'uninstall':
|
|
517
|
+
removeHooks(options);
|
|
518
|
+
break;
|
|
519
|
+
case 'status':
|
|
520
|
+
showStatus();
|
|
521
|
+
break;
|
|
522
|
+
case 'test':
|
|
523
|
+
testHook(args[1]);
|
|
524
|
+
break;
|
|
525
|
+
case 'help':
|
|
526
|
+
case '--help':
|
|
527
|
+
case '-h':
|
|
528
|
+
showHelp();
|
|
529
|
+
break;
|
|
530
|
+
default:
|
|
531
|
+
if (command) {
|
|
532
|
+
error(`Unknown command: ${command}`);
|
|
533
|
+
}
|
|
534
|
+
showHelp();
|
|
535
|
+
process.exit(command ? 1 : 0);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// ============================================================
|
|
540
|
+
// Exports
|
|
541
|
+
// ============================================================
|
|
542
|
+
|
|
543
|
+
module.exports = {
|
|
544
|
+
getHooksConfig,
|
|
545
|
+
setupHooks,
|
|
546
|
+
removeHooks,
|
|
547
|
+
showStatus,
|
|
548
|
+
testHook,
|
|
549
|
+
installForTarget,
|
|
550
|
+
removeForTarget,
|
|
551
|
+
checkIfInstalled
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
if (require.main === module) {
|
|
555
|
+
main();
|
|
556
|
+
}
|