wogiflow 2.10.0 → 2.11.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/state/bugfix-scope.json.template +7 -0
- package/.workflow/state/deploy-history.json.template +3 -0
- package/.workflow/state/deploy-routes.json.template +4 -0
- package/.workflow/state/strike-tracker.json.template +3 -0
- package/package.json +1 -1
- package/scripts/flow-config-defaults.js +56 -0
- package/scripts/flow-deploy-gate.js +335 -0
- package/scripts/flow-deploy-history.js +270 -0
- package/scripts/flow-hook-status.js +7 -1
- package/scripts/hooks/core/bugfix-scope-gate.js +427 -0
- package/scripts/hooks/core/deploy-gate.js +655 -0
- package/scripts/hooks/core/git-safety-gate.js +358 -0
- package/scripts/hooks/core/index.js +62 -0
- package/scripts/hooks/core/scope-mutation-gate.js +257 -0
- package/scripts/hooks/core/strike-gate.js +380 -0
- package/scripts/hooks/core/task-completed.js +41 -0
- package/scripts/hooks/entry/claude-code/post-tool-use.js +33 -0
- package/scripts/hooks/entry/claude-code/pre-tool-use.js +137 -0
package/package.json
CHANGED
|
@@ -187,12 +187,61 @@ const CONFIG_DEFAULTS = {
|
|
|
187
187
|
implementationGate: { enabled: true },
|
|
188
188
|
todoWriteGate: { enabled: true, blockImplementationWithoutTask: true },
|
|
189
189
|
routingGate: { enabled: true },
|
|
190
|
+
commitLogGate: { enabled: true },
|
|
190
191
|
loopEnforcement: { enabled: true },
|
|
191
192
|
hypothesisGate: {
|
|
192
193
|
enabled: true,
|
|
193
194
|
_comment_hypothesisGate: 'Blocks premature "fixed"/"should work" claims during bug investigation until hypothesis is verified. Pattern: hypothesis → verify → confirm → communicate.',
|
|
194
195
|
blockedPhrases: ['fixed', 'should work', 'go try', 'go refresh'],
|
|
195
196
|
requireExplicitVerification: true
|
|
197
|
+
},
|
|
198
|
+
deployGate: {
|
|
199
|
+
_comment_deployGate: 'Blocks deploy commands unless a valid HMAC-signed verification artifact exists. Off by default — opt in via `flow deploy-gate init`.',
|
|
200
|
+
enabled: false,
|
|
201
|
+
commands: [],
|
|
202
|
+
sourcePatterns: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue', '**/*.svelte', '**/*.css'],
|
|
203
|
+
requireForPriorities: ['P0', 'P1'],
|
|
204
|
+
blockWriteToVerifications: true,
|
|
205
|
+
minVerifiedRoutes: 3,
|
|
206
|
+
rejectLoginOnly: true
|
|
207
|
+
},
|
|
208
|
+
strikeEscalation: {
|
|
209
|
+
_comment_strikeEscalation: 'Mechanical strike counter. Blocks Edit/Write after repeated verification failures on the same task.',
|
|
210
|
+
enabled: true,
|
|
211
|
+
blockThreshold: 2,
|
|
212
|
+
escalateThreshold: 3,
|
|
213
|
+
hardBlockThreshold: 4,
|
|
214
|
+
productionCrashThreshold: 2
|
|
215
|
+
},
|
|
216
|
+
bugfixScope: {
|
|
217
|
+
_comment_bugfixScope: 'Pauses L3 bugfixes after N unique non-test files edited, requiring a scope inventory.',
|
|
218
|
+
enabled: true,
|
|
219
|
+
mode: 'warn',
|
|
220
|
+
fileThreshold: 3,
|
|
221
|
+
excludePatterns: ['*.test.*', '*.spec.*', '*.d.ts', '__tests__/**', '__mocks__/**'],
|
|
222
|
+
keywordMatchThreshold: 2,
|
|
223
|
+
fanOutThreshold: 10
|
|
224
|
+
},
|
|
225
|
+
revertFirst: {
|
|
226
|
+
_comment_revertFirst: 'For production crashes: recommends reverting before forward-fixing. Off by default.',
|
|
227
|
+
enabled: false,
|
|
228
|
+
keywords: ['production', 'crash', 'down', 'outage', '500 errors', 'users can\u0027t', 'site is broken', 'live issue'],
|
|
229
|
+
deployHistoryRetention: 50,
|
|
230
|
+
oldDeployWarningDays: 7
|
|
231
|
+
},
|
|
232
|
+
scopeMutation: {
|
|
233
|
+
_comment_scopeMutation: 'Agnostic gate: fix tasks creating 2+ new files → warn. Deleting pre-existing files → warn. No framework pattern matching.',
|
|
234
|
+
enabled: true,
|
|
235
|
+
newFileThreshold: 2,
|
|
236
|
+
mode: 'warn'
|
|
237
|
+
},
|
|
238
|
+
gitSafety: {
|
|
239
|
+
_comment_gitSafety: 'Auto-backup before destructive git operations. Prevents accidental loss of work from prior sessions.',
|
|
240
|
+
enabled: true,
|
|
241
|
+
maxBackwardCommits: 3,
|
|
242
|
+
ageThresholdHours: 24,
|
|
243
|
+
autoBackup: true,
|
|
244
|
+
maxBackupBranches: 3
|
|
196
245
|
}
|
|
197
246
|
},
|
|
198
247
|
|
|
@@ -342,6 +391,13 @@ const CONFIG_DEFAULTS = {
|
|
|
342
391
|
similarityThreshold: 0.8,
|
|
343
392
|
similarityWarningThreshold: 0.6
|
|
344
393
|
},
|
|
394
|
+
// --- Workspace Sovereignty ---
|
|
395
|
+
workspace: {
|
|
396
|
+
_comment_workerGatesSovereign: 'When true, workers cannot skip their own quality gates even if the manager instructs them to',
|
|
397
|
+
workerGatesSovereign: true,
|
|
398
|
+
managerCanOverrideLevel: false,
|
|
399
|
+
managerCanSkipGates: false
|
|
400
|
+
},
|
|
345
401
|
checkpoint: { enabled: false },
|
|
346
402
|
regressionTesting: { enabled: false },
|
|
347
403
|
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Deploy Gate CLI
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* flow deploy-gate init — Interactive setup wizard
|
|
8
|
+
* flow deploy-gate status — Show gate status and artifact info
|
|
9
|
+
* flow deploy-gate verify — Check if a valid artifact exists
|
|
10
|
+
* flow deploy-gate routes — Show route inventory
|
|
11
|
+
* flow deploy-gate add-route <path> — Manually add a route
|
|
12
|
+
* flow deploy-gate history — Show deploy history
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const fs = require('node:fs');
|
|
18
|
+
const path = require('node:path');
|
|
19
|
+
const { PATHS, getConfig, safeJsonParse, writeJson } = require('./flow-utils');
|
|
20
|
+
const {
|
|
21
|
+
isDeployGateEnabled,
|
|
22
|
+
getDeployGateConfig,
|
|
23
|
+
findLatestArtifact,
|
|
24
|
+
getRouteInventory,
|
|
25
|
+
addRoute,
|
|
26
|
+
getLastGoodDeploy,
|
|
27
|
+
DEPLOY_ROUTES_PATH,
|
|
28
|
+
DEPLOY_HISTORY_PATH
|
|
29
|
+
} = require('./hooks/core/deploy-gate');
|
|
30
|
+
|
|
31
|
+
// ============================================================
|
|
32
|
+
// CLI Commands
|
|
33
|
+
// ============================================================
|
|
34
|
+
|
|
35
|
+
function cmdInit() {
|
|
36
|
+
console.log('━━━ Deploy Gate Setup Wizard ━━━\n');
|
|
37
|
+
|
|
38
|
+
// 1. Detect package.json scripts
|
|
39
|
+
const pkgPath = path.join(PATHS.root, 'package.json');
|
|
40
|
+
const pkg = safeJsonParse(pkgPath, {});
|
|
41
|
+
const scripts = pkg.scripts || {};
|
|
42
|
+
const deployScripts = [];
|
|
43
|
+
|
|
44
|
+
for (const [name, cmd] of Object.entries(scripts)) {
|
|
45
|
+
if (/deploy|publish|release|sync|push/i.test(name) || /deploy|s3.*sync|vercel|netlify|fly.*deploy/i.test(cmd)) {
|
|
46
|
+
deployScripts.push({ name, cmd });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (deployScripts.length > 0) {
|
|
51
|
+
console.log('Detected deploy-related scripts:');
|
|
52
|
+
for (const s of deployScripts) {
|
|
53
|
+
console.log(` - npm run ${s.name}: ${s.cmd}`);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
console.log('No deploy scripts detected in package.json.');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 2. Suggest common deploy command patterns
|
|
60
|
+
const suggestedCommands = [];
|
|
61
|
+
for (const s of deployScripts) {
|
|
62
|
+
suggestedCommands.push(`npm run ${s.name}`);
|
|
63
|
+
// Also extract the raw command for direct matching
|
|
64
|
+
if (s.cmd) suggestedCommands.push(s.cmd.split('&&')[0].trim());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add common defaults if none detected
|
|
68
|
+
if (suggestedCommands.length === 0) {
|
|
69
|
+
suggestedCommands.push(
|
|
70
|
+
'aws s3 sync',
|
|
71
|
+
'vercel deploy',
|
|
72
|
+
'netlify deploy',
|
|
73
|
+
'fly deploy',
|
|
74
|
+
'docker push'
|
|
75
|
+
);
|
|
76
|
+
console.log('\nSuggested common deploy patterns (add the ones your project uses):');
|
|
77
|
+
for (const cmd of suggestedCommands) {
|
|
78
|
+
console.log(` - ${cmd}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 3. Detect framework
|
|
83
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
84
|
+
let framework = 'unknown';
|
|
85
|
+
if (deps['next']) framework = 'nextjs';
|
|
86
|
+
else if (deps['vite'] || deps['@vitejs/plugin-react']) framework = 'vite';
|
|
87
|
+
else if (deps['@angular/core']) framework = 'angular';
|
|
88
|
+
else if (deps['vue']) framework = 'vue';
|
|
89
|
+
else if (deps['svelte']) framework = 'svelte';
|
|
90
|
+
else if (deps['express'] || deps['fastify'] || deps['@nestjs/core']) framework = 'backend';
|
|
91
|
+
|
|
92
|
+
console.log(`\nDetected framework: ${framework}`);
|
|
93
|
+
|
|
94
|
+
// 4. Suggest verification method
|
|
95
|
+
let verificationMethod = 'checklist';
|
|
96
|
+
if (deps['playwright'] || deps['@playwright/test']) verificationMethod = 'playwright';
|
|
97
|
+
if (framework === 'backend') verificationMethod = 'api-test';
|
|
98
|
+
|
|
99
|
+
console.log(`Suggested verification method: ${verificationMethod}`);
|
|
100
|
+
|
|
101
|
+
// 5. Generate initial route inventory from common patterns
|
|
102
|
+
console.log('\nScanning for routes...');
|
|
103
|
+
const routes = scanForRoutes(framework);
|
|
104
|
+
if (routes.length > 0) {
|
|
105
|
+
console.log(`Found ${routes.length} route(s):`);
|
|
106
|
+
for (const r of routes) {
|
|
107
|
+
console.log(` - ${r}`);
|
|
108
|
+
addRoute(r, 'init-wizard');
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
console.log('No routes auto-detected. Add routes manually with: flow deploy-gate add-route <path>');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 6. Write suggested config
|
|
115
|
+
console.log('\n━━━ Suggested Configuration ━━━');
|
|
116
|
+
console.log('Add to .workflow/config.json under "enforcement":\n');
|
|
117
|
+
const suggestedConfig = {
|
|
118
|
+
deployGate: {
|
|
119
|
+
enabled: true,
|
|
120
|
+
commands: suggestedCommands.slice(0, 5),
|
|
121
|
+
sourcePatterns: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue', '**/*.svelte', '**/*.css'],
|
|
122
|
+
requireForPriorities: ['P0', 'P1'],
|
|
123
|
+
blockWriteToVerifications: true
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
console.log(JSON.stringify(suggestedConfig, null, 2));
|
|
127
|
+
|
|
128
|
+
console.log('\n━━━ Setup Complete ━━━');
|
|
129
|
+
console.log('Next steps:');
|
|
130
|
+
console.log(' 1. Add the config above to .workflow/config.json');
|
|
131
|
+
console.log(' 2. Customize the commands list for your deploy workflow');
|
|
132
|
+
console.log(' 3. Run `flow deploy-gate status` to verify setup');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function cmdStatus() {
|
|
136
|
+
const config = getConfig();
|
|
137
|
+
const enabled = isDeployGateEnabled(config);
|
|
138
|
+
const gateConfig = getDeployGateConfig(config);
|
|
139
|
+
|
|
140
|
+
console.log('━━━ Deploy Gate Status ━━━\n');
|
|
141
|
+
console.log(`Enabled: ${enabled ? '✓ YES' : '✗ NO'}`);
|
|
142
|
+
|
|
143
|
+
if (!enabled) {
|
|
144
|
+
console.log('\nRun `flow deploy-gate init` to set up the deploy gate.');
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.log(`Commands blocked: ${gateConfig.commands.length > 0 ? gateConfig.commands.join(', ') : '(none configured)'}`);
|
|
149
|
+
console.log(`Require verification for: ${gateConfig.requireForPriorities.join(', ')}`);
|
|
150
|
+
console.log(`Block Write to artifacts: ${gateConfig.blockWriteToVerifications}`);
|
|
151
|
+
|
|
152
|
+
// Check for valid artifact
|
|
153
|
+
const artifactResult = findLatestArtifact();
|
|
154
|
+
console.log(`\nLatest artifact: ${artifactResult.found ? '✓ VALID' : '✗ ' + (artifactResult.reason || 'NOT FOUND')}`);
|
|
155
|
+
if (artifactResult.found) {
|
|
156
|
+
const a = artifactResult.artifact;
|
|
157
|
+
console.log(` Method: ${a.method}`);
|
|
158
|
+
console.log(` Task: ${a.taskId}`);
|
|
159
|
+
console.log(` Created: ${a.createdAt}`);
|
|
160
|
+
console.log(` Routes verified: ${(a.routes || []).length}`);
|
|
161
|
+
console.log(` Evidence tier: ${a.evidenceTier}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Route inventory
|
|
165
|
+
const inventory = getRouteInventory();
|
|
166
|
+
console.log(`\nRoute inventory: ${inventory.routes.length} route(s)`);
|
|
167
|
+
|
|
168
|
+
// Deploy history
|
|
169
|
+
const lastDeploy = getLastGoodDeploy();
|
|
170
|
+
if (lastDeploy.found) {
|
|
171
|
+
console.log(`Last deploy: ${lastDeploy.deploy.commitHash.slice(0, 8)} (${lastDeploy.deploy.timestamp})`);
|
|
172
|
+
} else {
|
|
173
|
+
console.log('Last deploy: (no history)');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function cmdVerify() {
|
|
178
|
+
const artifactResult = findLatestArtifact();
|
|
179
|
+
if (artifactResult.found) {
|
|
180
|
+
console.log('✓ Valid verification artifact found');
|
|
181
|
+
console.log(JSON.stringify(artifactResult.artifact, null, 2));
|
|
182
|
+
process.exit(0);
|
|
183
|
+
} else {
|
|
184
|
+
console.log(`✗ No valid artifact: ${artifactResult.reason}`);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function cmdRoutes() {
|
|
190
|
+
const inventory = getRouteInventory();
|
|
191
|
+
console.log('━━━ Route Inventory ━━━\n');
|
|
192
|
+
if (inventory.routes.length === 0) {
|
|
193
|
+
console.log('No routes registered. Add with: flow deploy-gate add-route <path>');
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
for (const r of inventory.routes) {
|
|
197
|
+
console.log(` ${r.path} — added ${r.addedAt} (${r.source})`);
|
|
198
|
+
}
|
|
199
|
+
console.log(`\nTotal: ${inventory.routes.length} route(s)`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function cmdAddRoute(routePath) {
|
|
203
|
+
if (!routePath) {
|
|
204
|
+
console.error('Usage: flow deploy-gate add-route <path>');
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
const added = addRoute(routePath, 'manual');
|
|
208
|
+
if (added) {
|
|
209
|
+
console.log(`✓ Added route: ${routePath}`);
|
|
210
|
+
} else {
|
|
211
|
+
console.log(`Route already exists: ${routePath}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function cmdHistory() {
|
|
216
|
+
const history = safeJsonParse(DEPLOY_HISTORY_PATH, { deploys: [] });
|
|
217
|
+
console.log('━━━ Deploy History ━━━\n');
|
|
218
|
+
if (history.deploys.length === 0) {
|
|
219
|
+
console.log('No deploy history recorded.');
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
for (const d of history.deploys.slice(0, 10)) {
|
|
223
|
+
console.log(` ${d.commitHash.slice(0, 8)} | ${d.timestamp} | ${d.environment}`);
|
|
224
|
+
}
|
|
225
|
+
console.log(`\nShowing ${Math.min(10, history.deploys.length)} of ${history.deploys.length} deploys`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ============================================================
|
|
229
|
+
// Route Scanning Helpers
|
|
230
|
+
// ============================================================
|
|
231
|
+
|
|
232
|
+
function scanForRoutes(framework) {
|
|
233
|
+
const routes = [];
|
|
234
|
+
const { execSync } = require('node:child_process');
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
// Next.js: pages or app directory
|
|
238
|
+
if (framework === 'nextjs') {
|
|
239
|
+
const patterns = ['app/**/page.tsx', 'app/**/page.jsx', 'pages/**/*.tsx', 'pages/**/*.jsx', 'src/app/**/page.tsx'];
|
|
240
|
+
for (const pattern of patterns) {
|
|
241
|
+
try {
|
|
242
|
+
const files = execSync(`git ls-files '${pattern}'`, { encoding: 'utf-8', cwd: PATHS.root }).trim();
|
|
243
|
+
if (files) {
|
|
244
|
+
for (const f of files.split('\n')) {
|
|
245
|
+
const route = '/' + f.replace(/^(src\/)?(app|pages)\//, '').replace(/(page|index)\.(tsx|jsx)$/, '').replace(/\/$/, '') || '/';
|
|
246
|
+
if (!routes.includes(route)) routes.push(route);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
} catch (_err) {
|
|
250
|
+
// Pattern didn't match
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// React Router / Vite: look for route definitions
|
|
256
|
+
if (framework === 'vite' || framework === 'unknown') {
|
|
257
|
+
try {
|
|
258
|
+
const routeFiles = execSync("git ls-files | grep -iE '(router|routes|App)\\.(tsx|jsx|ts|js)$'", { encoding: 'utf-8', cwd: PATHS.root }).trim();
|
|
259
|
+
if (routeFiles) {
|
|
260
|
+
for (const f of routeFiles.split('\n')) {
|
|
261
|
+
try {
|
|
262
|
+
const content = fs.readFileSync(path.join(PATHS.root, f), 'utf-8');
|
|
263
|
+
const pathMatches = content.matchAll(/path:\s*['"]([^'"]+)['"]/g);
|
|
264
|
+
for (const m of pathMatches) {
|
|
265
|
+
if (!routes.includes(m[1])) routes.push(m[1]);
|
|
266
|
+
}
|
|
267
|
+
} catch (_err) {
|
|
268
|
+
// Skip unreadable files
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
} catch (_err) {
|
|
273
|
+
// No route files found
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Express/backend: look for route handlers
|
|
278
|
+
if (framework === 'backend') {
|
|
279
|
+
try {
|
|
280
|
+
const apiFiles = execSync("git ls-files | grep -iE '(routes|controller|api).*\\.(ts|js)$'", { encoding: 'utf-8', cwd: PATHS.root }).trim();
|
|
281
|
+
if (apiFiles) {
|
|
282
|
+
for (const f of apiFiles.split('\n').slice(0, 20)) {
|
|
283
|
+
try {
|
|
284
|
+
const content = fs.readFileSync(path.join(PATHS.root, f), 'utf-8');
|
|
285
|
+
const pathMatches = content.matchAll(/\.(get|post|put|patch|delete)\s*\(\s*['"]([^'"]+)['"]/gi);
|
|
286
|
+
for (const m of pathMatches) {
|
|
287
|
+
const route = `${m[1].toUpperCase()} ${m[2]}`;
|
|
288
|
+
if (!routes.includes(route)) routes.push(route);
|
|
289
|
+
}
|
|
290
|
+
} catch (_err) {
|
|
291
|
+
// Skip unreadable files
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
} catch (_err) {
|
|
296
|
+
// No API files found
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
} catch (_err) {
|
|
300
|
+
// Scanning failed — non-critical
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return routes;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ============================================================
|
|
307
|
+
// CLI Entrypoint
|
|
308
|
+
// ============================================================
|
|
309
|
+
|
|
310
|
+
const args = process.argv.slice(2);
|
|
311
|
+
const command = args[0];
|
|
312
|
+
|
|
313
|
+
switch (command) {
|
|
314
|
+
case 'init':
|
|
315
|
+
cmdInit();
|
|
316
|
+
break;
|
|
317
|
+
case 'status':
|
|
318
|
+
cmdStatus();
|
|
319
|
+
break;
|
|
320
|
+
case 'verify':
|
|
321
|
+
cmdVerify();
|
|
322
|
+
break;
|
|
323
|
+
case 'routes':
|
|
324
|
+
cmdRoutes();
|
|
325
|
+
break;
|
|
326
|
+
case 'add-route':
|
|
327
|
+
cmdAddRoute(args[1]);
|
|
328
|
+
break;
|
|
329
|
+
case 'history':
|
|
330
|
+
cmdHistory();
|
|
331
|
+
break;
|
|
332
|
+
default:
|
|
333
|
+
console.log('Usage: flow deploy-gate <init|status|verify|routes|add-route|history>');
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|