wogiflow 1.8.7 → 1.8.9
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/.claude/commands/wogi-start.md +9 -0
- package/.claude/docs/explore-agents.md +32 -12
- package/.workflow/package.json +3 -0
- package/lib/installer.js +13 -0
- package/package.json +2 -1
- package/scripts/flow-community.js +14 -2
- package/scripts/hooks/core/component-check.js +98 -10
- package/scripts/hooks/core/routing-gate.js +4 -2
- package/scripts/hooks/entry/claude-code/user-prompt-submit.js +3 -2
- package/scripts/postinstall.js +19 -1
|
@@ -162,6 +162,15 @@ Launch all in parallel. When `config.hybrid.enabled`, route via `model` paramete
|
|
|
162
162
|
|
|
163
163
|
**After agents complete**: Display consolidated research summary covering codebase analysis, best practices, version info, risks, standards, and consumer impact.
|
|
164
164
|
|
|
165
|
+
**REUSE GATE (MANDATORY)**: After consolidating agent results, check for reuse candidates:
|
|
166
|
+
1. Collect all reuse candidates reported by Agent 1 (domain-keyword search) and Agent 5 (registry scan)
|
|
167
|
+
2. If ANY reuse candidate has purpose overlap with planned new code → **STOP and present to user**:
|
|
168
|
+
- Show each candidate: name, path, purpose, similarity
|
|
169
|
+
- Ask: "Use existing / Extend existing / Create new (explain why)"
|
|
170
|
+
- Implementation BLOCKED until user decides on each candidate
|
|
171
|
+
3. If no reuse candidates found → proceed normally
|
|
172
|
+
4. This gate runs BEFORE spec generation — catching reuse early prevents wasted implementation
|
|
173
|
+
|
|
165
174
|
**For L1/L0 tasks**: Offer to deepen research (exhaustive search, load all skills, full dependency tree).
|
|
166
175
|
|
|
167
176
|
**Fallback**: If agents fail, log warning and proceed with remaining. Consumer Impact failure on refactor tasks = HARD BLOCK (require user confirmation). See `.claude/docs/explore-agents.md` for details.
|
|
@@ -9,23 +9,43 @@ Launch as `Agent(subagent_type=Explore)`:
|
|
|
9
9
|
```
|
|
10
10
|
Analyze the codebase for task: "[TASK_TITLE]"
|
|
11
11
|
|
|
12
|
-
1
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
STEP 1 — Domain-keyword search (MANDATORY, do this FIRST):
|
|
13
|
+
a. Extract 3-5 domain keywords from the task title
|
|
14
|
+
Example: "AI Policies tab" → ["policy", "policies", "automation", "AI"]
|
|
15
|
+
Example: "Payment service refactor" → ["payment", "checkout", "billing", "transaction"]
|
|
16
|
+
b. For EACH keyword, run:
|
|
17
|
+
- Glob **/*[keyword]* to find files with that keyword in the name
|
|
18
|
+
- Grep for the keyword in src/ (or project root) to find references in code
|
|
19
|
+
c. Read EVERY file that matches — these are potential reuse candidates
|
|
20
|
+
d. Pay special attention to files in shared/, utils/, lib/, common/ directories
|
|
21
|
+
|
|
22
|
+
STEP 2 — Registry check (read ALL registry maps):
|
|
23
|
+
a. Read app-map.md for existing components that could be reused
|
|
24
|
+
b. Read function-map.md for existing utility functions
|
|
25
|
+
c. Read api-map.md for existing API endpoints
|
|
26
|
+
d. Read any other *-map.md files in .workflow/state/
|
|
27
|
+
e. For each planned NEW item, check if something similar already exists
|
|
28
|
+
|
|
29
|
+
STEP 3 — Pattern & dependency analysis:
|
|
30
|
+
a. Read decisions.md for patterns that must be followed
|
|
31
|
+
b. Map dependencies:
|
|
32
|
+
- Files that REFERENCE the target code
|
|
33
|
+
- Files REFERENCED BY the target code
|
|
34
|
+
c. Surface assumptions that need verification
|
|
22
35
|
|
|
23
36
|
Return a structured summary:
|
|
37
|
+
- **REUSE CANDIDATES** (MUST be first section):
|
|
38
|
+
List every existing file/component/function/service that overlaps
|
|
39
|
+
with what this task plans to create. For each: path, purpose, and
|
|
40
|
+
whether the task should USE it, EXTEND it, or CREATE new.
|
|
24
41
|
- Related files (path + why it's relevant)
|
|
25
|
-
-
|
|
26
|
-
- Patterns to follow
|
|
42
|
+
- Patterns to follow (from decisions.md)
|
|
27
43
|
- Dependency map
|
|
28
44
|
- Assumptions to verify
|
|
45
|
+
|
|
46
|
+
CRITICAL: If domain-keyword search finds existing implementations that
|
|
47
|
+
overlap with the task's goals, this MUST be prominently flagged.
|
|
48
|
+
Do NOT skip Step 1 even if you think you already know the codebase.
|
|
29
49
|
```
|
|
30
50
|
|
|
31
51
|
## Agent 2: Best Practices Researcher
|
package/lib/installer.js
CHANGED
|
@@ -1110,6 +1110,19 @@ function createWorkflowStructure(projectRoot, config) {
|
|
|
1110
1110
|
fs.mkdirSync(path.join(workflowDir, dir), { recursive: true });
|
|
1111
1111
|
}
|
|
1112
1112
|
|
|
1113
|
+
// Ensure .workflow/ uses CommonJS — required when project root has "type": "module"
|
|
1114
|
+
// Without this, Node.js inherits ESM from the project root and .workflow/bridges/*.js
|
|
1115
|
+
// (which use require/module.exports) crash with "require is not defined".
|
|
1116
|
+
// Same pattern as scripts/package.json.
|
|
1117
|
+
const workflowPkgPath = path.join(workflowDir, 'package.json');
|
|
1118
|
+
try {
|
|
1119
|
+
fs.writeFileSync(workflowPkgPath, JSON.stringify({ type: 'commonjs' }, null, 2) + '\n', { flag: 'wx' });
|
|
1120
|
+
} catch (err) {
|
|
1121
|
+
if (err.code !== 'EEXIST') {
|
|
1122
|
+
throw err;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1113
1126
|
// Create config.json using shared defaults with project-specific overrides
|
|
1114
1127
|
const configPath = path.join(workflowDir, 'config.json');
|
|
1115
1128
|
const configContent = getDefaultConfig({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wogiflow",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.9",
|
|
4
4
|
"description": "AI-powered development workflow management system with multi-model support",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"lib/",
|
|
20
20
|
"scripts/",
|
|
21
21
|
"templates/",
|
|
22
|
+
".workflow/package.json",
|
|
22
23
|
".workflow/templates/",
|
|
23
24
|
".workflow/agents/",
|
|
24
25
|
".workflow/bridges/",
|
|
@@ -257,18 +257,30 @@ function collectShareableData(config) {
|
|
|
257
257
|
|
|
258
258
|
/**
|
|
259
259
|
* Get WogiFlow version from package.json.
|
|
260
|
+
* Uses require.resolve to find the actual wogiflow package, not the host project.
|
|
260
261
|
* @returns {string}
|
|
261
262
|
*/
|
|
262
263
|
let _cachedVersion = null;
|
|
263
264
|
function getWogiFlowVersion() {
|
|
264
265
|
if (_cachedVersion) return _cachedVersion;
|
|
265
266
|
try {
|
|
266
|
-
const pkgPath =
|
|
267
|
+
const pkgPath = require.resolve('wogiflow/package.json');
|
|
267
268
|
const pkg = safeJsonParse(pkgPath, {});
|
|
268
269
|
_cachedVersion = pkg.version || 'unknown';
|
|
269
270
|
return _cachedVersion;
|
|
270
271
|
} catch {
|
|
271
|
-
|
|
272
|
+
// Fallback: resolve relative to this file (works when running from source repo)
|
|
273
|
+
try {
|
|
274
|
+
const fallbackPath = path.join(__dirname, '..', 'package.json');
|
|
275
|
+
const pkg = safeJsonParse(fallbackPath, {});
|
|
276
|
+
if (pkg.name === 'wogiflow') {
|
|
277
|
+
_cachedVersion = pkg.version || 'unknown';
|
|
278
|
+
} else {
|
|
279
|
+
_cachedVersion = 'unknown';
|
|
280
|
+
}
|
|
281
|
+
} catch {
|
|
282
|
+
_cachedVersion = 'unknown';
|
|
283
|
+
}
|
|
272
284
|
return _cachedVersion;
|
|
273
285
|
}
|
|
274
286
|
}
|
|
@@ -36,14 +36,66 @@ function isComponentCheckEnabled(config) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* Default reusable-code path patterns by project type.
|
|
40
|
+
* These cover ALL areas where reusable code lives — not just UI components.
|
|
41
|
+
* Projects can override via config.componentReuse.patterns.
|
|
42
|
+
*/
|
|
43
|
+
const DEFAULT_REUSABLE_PATTERNS = {
|
|
44
|
+
// Frontend-specific
|
|
45
|
+
frontend: [
|
|
46
|
+
'**/components/**', '**/ui/**', '**/src/components/**',
|
|
47
|
+
'**/pages/**/shared/**', '**/features/**/shared/**',
|
|
48
|
+
'**/layouts/**', '**/modals/**', '**/widgets/**'
|
|
49
|
+
],
|
|
50
|
+
// Backend-specific
|
|
51
|
+
backend: [
|
|
52
|
+
'**/services/**', '**/middleware/**',
|
|
53
|
+
'**/models/**', '**/schemas/**', '**/validators/**',
|
|
54
|
+
'**/routes/**', '**/controllers/**',
|
|
55
|
+
'**/repositories/**', '**/providers/**'
|
|
56
|
+
],
|
|
57
|
+
// Universal (applies to ALL project types)
|
|
58
|
+
universal: [
|
|
59
|
+
'**/utils/**', '**/helpers/**', '**/lib/**', '**/shared/**',
|
|
60
|
+
'**/hooks/**', '**/composables/**',
|
|
61
|
+
'**/api/**', '**/clients/**',
|
|
62
|
+
'**/types/**', '**/interfaces/**',
|
|
63
|
+
'**/constants/**', '**/config/**'
|
|
64
|
+
]
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get reusable-code patterns to check.
|
|
69
|
+
* Combines universal patterns with project-type-specific patterns.
|
|
70
|
+
* Projects can override entirely via config.componentReuse.patterns.
|
|
40
71
|
* @param {Object} [config] - Pre-loaded config (optional, falls back to getConfig())
|
|
41
|
-
* @returns {string[]} Glob patterns for
|
|
72
|
+
* @returns {string[]} Glob patterns for reusable code directories
|
|
42
73
|
*/
|
|
43
74
|
function getComponentPatterns(config) {
|
|
44
75
|
if (!config) config = getConfig();
|
|
45
|
-
|
|
46
|
-
|
|
76
|
+
|
|
77
|
+
// If user explicitly configured patterns, use those
|
|
78
|
+
if (config.componentReuse?.patterns && Array.isArray(config.componentReuse.patterns)) {
|
|
79
|
+
return config.componentReuse.patterns;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Build patterns from project type
|
|
83
|
+
const projectType = config.projectType || 'unknown';
|
|
84
|
+
const patterns = [...DEFAULT_REUSABLE_PATTERNS.universal];
|
|
85
|
+
|
|
86
|
+
if (projectType === 'frontend' || projectType === 'fullstack' || projectType === 'unknown') {
|
|
87
|
+
patterns.push(...DEFAULT_REUSABLE_PATTERNS.frontend);
|
|
88
|
+
}
|
|
89
|
+
if (projectType === 'backend' || projectType === 'fullstack' || projectType === 'unknown') {
|
|
90
|
+
patterns.push(...DEFAULT_REUSABLE_PATTERNS.backend);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Add any extra patterns from config (additive)
|
|
94
|
+
if (config.componentReuse?.extraPatterns && Array.isArray(config.componentReuse.extraPatterns)) {
|
|
95
|
+
patterns.push(...config.componentReuse.extraPatterns);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return patterns;
|
|
47
99
|
}
|
|
48
100
|
|
|
49
101
|
/**
|
|
@@ -87,17 +139,53 @@ function isComponentPath(filePath, config) {
|
|
|
87
139
|
}
|
|
88
140
|
|
|
89
141
|
/**
|
|
90
|
-
* Load the component index
|
|
142
|
+
* Load the component index.
|
|
143
|
+
* Falls back to building a minimal index from all registry maps when
|
|
144
|
+
* component-index.json doesn't exist.
|
|
91
145
|
* @returns {Object|null} Component index or null
|
|
92
146
|
*/
|
|
93
147
|
function loadComponentIndex() {
|
|
94
148
|
try {
|
|
95
149
|
const indexPath = path.join(PATHS.state, 'component-index.json');
|
|
96
|
-
if (
|
|
97
|
-
return
|
|
150
|
+
if (fs.existsSync(indexPath)) {
|
|
151
|
+
return JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Fallback: build a minimal index from all *-map.md files in state/
|
|
155
|
+
const stateDir = PATHS.state;
|
|
156
|
+
if (!stateDir || !fs.existsSync(stateDir)) return null;
|
|
157
|
+
|
|
158
|
+
const components = [];
|
|
159
|
+
const entries = fs.readdirSync(stateDir);
|
|
160
|
+
for (const entry of entries) {
|
|
161
|
+
if (entry.endsWith('-map.md') && /^[a-z0-9-]+\.md$/.test(entry)) {
|
|
162
|
+
try {
|
|
163
|
+
const content = fs.readFileSync(path.join(stateDir, entry), 'utf-8');
|
|
164
|
+
const lines = content.split('\n');
|
|
165
|
+
for (const line of lines) {
|
|
166
|
+
// Parse table rows: | Name | description | path |
|
|
167
|
+
const tableMatch = line.match(/^\|\s*([^|]+)\s*\|\s*([^|]*)\s*\|\s*([^|]*)\s*\|/);
|
|
168
|
+
if (tableMatch && !tableMatch[1].includes('---')) {
|
|
169
|
+
const name = tableMatch[1].trim();
|
|
170
|
+
if (name && name !== 'Component' && name !== 'Name' && name !== 'Function' && name !== 'Endpoint') {
|
|
171
|
+
components.push({
|
|
172
|
+
name,
|
|
173
|
+
description: (tableMatch[2] || '').trim(),
|
|
174
|
+
path: (tableMatch[3] || '').trim(),
|
|
175
|
+
source: entry.replace('.md', '')
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch (err) {
|
|
181
|
+
// Skip unreadable map files
|
|
182
|
+
}
|
|
183
|
+
}
|
|
98
184
|
}
|
|
99
|
-
|
|
100
|
-
|
|
185
|
+
|
|
186
|
+
if (components.length === 0) return null;
|
|
187
|
+
return { components, source: 'fallback-from-maps' };
|
|
188
|
+
} catch (err) {
|
|
101
189
|
return null;
|
|
102
190
|
}
|
|
103
191
|
}
|
|
@@ -136,7 +224,7 @@ function parseAppMap() {
|
|
|
136
224
|
}
|
|
137
225
|
|
|
138
226
|
return components;
|
|
139
|
-
} catch (
|
|
227
|
+
} catch (err) {
|
|
140
228
|
return [];
|
|
141
229
|
}
|
|
142
230
|
}
|
|
@@ -75,8 +75,10 @@ function hasActiveTask() {
|
|
|
75
75
|
if (process.env.DEBUG) {
|
|
76
76
|
console.error(`[routing-gate] Ready data read error: ${err.message}`);
|
|
77
77
|
}
|
|
78
|
-
// Fail-
|
|
79
|
-
|
|
78
|
+
// Fail-closed: if can't read ready.json, assume no active task.
|
|
79
|
+
// Fail-open here was a bypass vector — corrupted ready.json would make
|
|
80
|
+
// subagent routing think a task exists and skip the routing gate.
|
|
81
|
+
return false;
|
|
80
82
|
}
|
|
81
83
|
}
|
|
82
84
|
|
|
@@ -52,7 +52,7 @@ async function main() {
|
|
|
52
52
|
process.exit(0);
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
|
-
} catch (
|
|
55
|
+
} catch (err) {
|
|
56
56
|
// Parse error - allow through (graceful degradation)
|
|
57
57
|
console.log(JSON.stringify({ continue: true, hookSpecificOutput: { hookEventName: 'UserPromptSubmit' } }));
|
|
58
58
|
process.exit(0);
|
|
@@ -118,7 +118,8 @@ async function main() {
|
|
|
118
118
|
|
|
119
119
|
// v6.0: Set routing-pending flag for routing gate enforcement
|
|
120
120
|
// This blocks ALL gated tool calls until a /wogi-* skill is invoked
|
|
121
|
-
// v8.0: Always set, even with active tasks — every turn must route through /wogi-start
|
|
121
|
+
// v8.0: Always set, even with active tasks — every turn must route through /wogi-start.
|
|
122
|
+
// Exception: skipped when the prompt IS a /wogi-* command (see isWogiCommand below).
|
|
122
123
|
// v6.1: Also skip when the prompt IS a /wogi-* command — the user is already routing.
|
|
123
124
|
// When users type "/wogi-start ..." directly, Claude Code expands the skill inline
|
|
124
125
|
// (not through the Skill tool), so clearRoutingPending() in PreToolUse never fires.
|
package/scripts/postinstall.js
CHANGED
|
@@ -134,7 +134,12 @@ function createMinimalStructure() {
|
|
|
134
134
|
? fs.readFileSync(templatePath, 'utf-8')
|
|
135
135
|
: JSON.stringify({ prompts: [], version: 1 }, null, 2);
|
|
136
136
|
// Validate template is valid JSON before writing (with proto check)
|
|
137
|
-
|
|
137
|
+
let parsed;
|
|
138
|
+
try {
|
|
139
|
+
parsed = JSON.parse(rawContent);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
parsed = { prompts: [], version: 1 };
|
|
142
|
+
}
|
|
138
143
|
if (parsed && typeof parsed === 'object') {
|
|
139
144
|
for (const k of Object.keys(parsed)) {
|
|
140
145
|
if (k === '__proto__' || k === 'constructor' || k === 'prototype') delete parsed[k];
|
|
@@ -384,6 +389,19 @@ function copyWorkflowManagedDirs() {
|
|
|
384
389
|
copyDir(src, dest, false);
|
|
385
390
|
}
|
|
386
391
|
}
|
|
392
|
+
|
|
393
|
+
// Ensure .workflow/package.json exists with "type": "commonjs".
|
|
394
|
+
// Required when the project root has "type": "module" — without it,
|
|
395
|
+
// Node.js inherits ESM and .workflow/bridges/*.js (CJS) crash.
|
|
396
|
+
const workflowPkg = path.join(WORKFLOW_DIR, 'package.json');
|
|
397
|
+
try {
|
|
398
|
+
fs.writeFileSync(workflowPkg, JSON.stringify({ type: 'commonjs' }, null, 2) + '\n', { flag: 'wx' });
|
|
399
|
+
} catch (err) {
|
|
400
|
+
if (err.code !== 'EEXIST') {
|
|
401
|
+
console.warn(`[postinstall] Warning: Failed to create .workflow/package.json: ${err.message}`);
|
|
402
|
+
console.warn('[postinstall] ESM projects may have issues with WogiFlow hooks. Create this file manually with: {"type":"commonjs"}');
|
|
403
|
+
}
|
|
404
|
+
}
|
|
387
405
|
}
|
|
388
406
|
|
|
389
407
|
/**
|