wogiflow 1.5.5 → 1.5.7
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/rules/README.md +60 -0
- package/.claude/rules/architecture/component-reuse.md +38 -0
- package/.claude/rules/architecture/document-structure.md +76 -0
- package/.claude/rules/architecture/feature-refactoring-cleanup.md +87 -0
- package/.claude/rules/architecture/model-management.md +35 -0
- package/.claude/rules/architecture/self-maintenance.md +87 -0
- package/.claude/rules/code-style/naming-conventions.md +55 -0
- package/.claude/rules/security/security-patterns.md +176 -0
- package/.claude/settings.json +2 -2
- package/.claude/skills/figma-analyzer/knowledge/learnings.md +11 -0
- package/.workflow/agents/performance.md +112 -0
- package/.workflow/specs/architecture.md.template +24 -0
- package/.workflow/specs/stack.md.template +33 -0
- package/.workflow/specs/testing.md.template +36 -0
- package/package.json +1 -1
- package/scripts/hooks/core/routing-gate.js +12 -5
- package/scripts/hooks/entry/claude-code/user-prompt-submit.js +3 -1
- package/scripts/postinstall.js +79 -8
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Project Rules
|
|
2
|
+
|
|
3
|
+
This directory contains coding rules and patterns for this project, organized by category.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
.claude/rules/
|
|
9
|
+
├── code-style/ # Naming conventions, formatting
|
|
10
|
+
│ └── naming-conventions.md
|
|
11
|
+
├── security/ # Security patterns and practices
|
|
12
|
+
│ └── security-patterns.md
|
|
13
|
+
├── architecture/ # Design decisions and patterns
|
|
14
|
+
│ ├── component-reuse.md
|
|
15
|
+
│ └── model-management.md
|
|
16
|
+
└── README.md
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## How Rules Work
|
|
20
|
+
|
|
21
|
+
Rules are automatically loaded by Claude Code based on:
|
|
22
|
+
- **alwaysApply: true** - Rule is always loaded
|
|
23
|
+
- **alwaysApply: false** - Rule is loaded based on `globs` or `description` relevance
|
|
24
|
+
- **globs** - File patterns that trigger rule loading
|
|
25
|
+
|
|
26
|
+
## Adding New Rules
|
|
27
|
+
|
|
28
|
+
1. Choose the appropriate category subdirectory
|
|
29
|
+
2. Create a `.md` file with frontmatter:
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
---
|
|
33
|
+
alwaysApply: false
|
|
34
|
+
description: "Brief description for relevance matching"
|
|
35
|
+
globs: src/**/*.ts # Optional: only load for these files
|
|
36
|
+
---
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
3. Write the rule content in markdown
|
|
40
|
+
|
|
41
|
+
## Categories
|
|
42
|
+
|
|
43
|
+
| Category | Purpose |
|
|
44
|
+
|----------|---------|
|
|
45
|
+
| code-style | Naming conventions, formatting, file structure |
|
|
46
|
+
| security | Security patterns, input validation, safe practices |
|
|
47
|
+
| architecture | Design decisions, component patterns, system organization |
|
|
48
|
+
|
|
49
|
+
## Auto-Generation
|
|
50
|
+
|
|
51
|
+
Some rules can be auto-generated from `.workflow/state/decisions.md`:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
node scripts/flow-rules-sync.js
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The sync script will route rules to appropriate category subdirectories.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
Last updated: 2026-01-12
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
globs: src/components/**/*
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
description: "Component reuse policy - always check app-map.md before creating components"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Component Reuse Policy
|
|
8
|
+
|
|
9
|
+
**Rule**: Always check `app-map.md` before creating any component.
|
|
10
|
+
|
|
11
|
+
## Priority Order
|
|
12
|
+
|
|
13
|
+
1. **Use existing** - Check if component already exists in app-map
|
|
14
|
+
2. **Add variant** - Extend existing component with a new variant
|
|
15
|
+
3. **Extend** - Create a wrapper/HOC around existing component
|
|
16
|
+
4. **Create new** - Only as last resort
|
|
17
|
+
|
|
18
|
+
## Before Creating Components
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Check app-map first
|
|
22
|
+
cat .workflow/state/app-map.md | grep -i "button"
|
|
23
|
+
|
|
24
|
+
# Or search codebase
|
|
25
|
+
grep -r "Button" src/components/
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Variant vs New Component
|
|
29
|
+
|
|
30
|
+
Prefer variants when:
|
|
31
|
+
- Same base functionality, different appearance
|
|
32
|
+
- Same HTML structure, different styling
|
|
33
|
+
- Same component, different size/color/state
|
|
34
|
+
|
|
35
|
+
Create new component when:
|
|
36
|
+
- Fundamentally different functionality
|
|
37
|
+
- Different DOM structure
|
|
38
|
+
- Different state management
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
alwaysApply: true
|
|
3
|
+
description: "All AI-context documents must use PIN markers for targeted context loading"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Document Structure for AI Context
|
|
7
|
+
|
|
8
|
+
All documents in `.workflow/` that are used as AI context MUST follow the PIN standard.
|
|
9
|
+
|
|
10
|
+
## Required Structure
|
|
11
|
+
|
|
12
|
+
### 1. Header with PIN List
|
|
13
|
+
Every document starts with a comment listing all pins in the document:
|
|
14
|
+
```markdown
|
|
15
|
+
<!-- PINS: pin1, pin2, pin3 -->
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### 2. Section PIN Markers
|
|
19
|
+
Each major section has a PIN marker comment:
|
|
20
|
+
```markdown
|
|
21
|
+
### Section Title
|
|
22
|
+
<!-- PIN: section-specific-pin -->
|
|
23
|
+
[Content]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 3. PIN Naming Convention
|
|
27
|
+
- Use kebab-case: `user-authentication`, not `userAuthentication`
|
|
28
|
+
- Use semantic names: `error-handling`, not `eh`
|
|
29
|
+
- Use compound names for specificity: `json-parse-safety`
|
|
30
|
+
|
|
31
|
+
## Why PINs Matter
|
|
32
|
+
|
|
33
|
+
The PIN system enables:
|
|
34
|
+
1. **Targeted context loading**: Only load sections relevant to current task
|
|
35
|
+
2. **Cheaper model routing**: Haiku can fetch only relevant sections for Opus
|
|
36
|
+
3. **Change detection**: Hash sections independently for smart invalidation
|
|
37
|
+
4. **Cross-reference**: Link sections by PIN across documents
|
|
38
|
+
|
|
39
|
+
## Example Document
|
|
40
|
+
|
|
41
|
+
```markdown
|
|
42
|
+
# Config Reference
|
|
43
|
+
|
|
44
|
+
<!-- PINS: database, authentication, api-keys, environment -->
|
|
45
|
+
|
|
46
|
+
## Database Settings
|
|
47
|
+
<!-- PIN: database -->
|
|
48
|
+
| Setting | Default | Description |
|
|
49
|
+
|---------|---------|-------------|
|
|
50
|
+
|
|
51
|
+
## Authentication
|
|
52
|
+
<!-- PIN: authentication -->
|
|
53
|
+
| Setting | Default | Description |
|
|
54
|
+
|---------|---------|-------------|
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Parsing
|
|
58
|
+
|
|
59
|
+
The PIN system automatically parses documents with:
|
|
60
|
+
- `flow-section-index.js` - Generates section index with pins
|
|
61
|
+
- `flow-section-resolver.js` - Resolves sections by PIN lookup
|
|
62
|
+
- `getSectionsByPins(['auth', 'security'])` - Fetch only relevant sections
|
|
63
|
+
|
|
64
|
+
## Files That Must Have PINs
|
|
65
|
+
|
|
66
|
+
| File | Required PINs |
|
|
67
|
+
|------|---------------|
|
|
68
|
+
| `decisions.md` | Per coding rule/pattern |
|
|
69
|
+
| `app-map.md` | Per component/screen |
|
|
70
|
+
| `product.md` | Per product section |
|
|
71
|
+
| `stack.md` | Per technology |
|
|
72
|
+
|
|
73
|
+
## Validation
|
|
74
|
+
|
|
75
|
+
Run `node scripts/flow-section-index.js --force` to regenerate the index.
|
|
76
|
+
Check `.workflow/state/section-index.json` for indexed sections and their pins.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
alwaysApply: false
|
|
3
|
+
description: "Cleanup checklist when refactoring or renaming features"
|
|
4
|
+
globs:
|
|
5
|
+
- "scripts/*.js"
|
|
6
|
+
- ".claude/skills/**/*"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Feature Refactoring Cleanup
|
|
10
|
+
|
|
11
|
+
When refactoring, renaming, or replacing a feature, ensure complete cleanup of the old implementation.
|
|
12
|
+
|
|
13
|
+
## Mandatory Cleanup Checklist
|
|
14
|
+
|
|
15
|
+
When a feature is refactored or renamed, you MUST:
|
|
16
|
+
|
|
17
|
+
### 1. Remove Old Code
|
|
18
|
+
- [ ] Delete old script files (e.g., `flow-old-feature.js`)
|
|
19
|
+
- [ ] Remove old skill directories (e.g., `.claude/skills/old-feature/`)
|
|
20
|
+
- [ ] Remove old hook files if applicable
|
|
21
|
+
|
|
22
|
+
### 2. Update Configuration
|
|
23
|
+
- [ ] Rename config keys (e.g., `oldFeature` → `newFeature`)
|
|
24
|
+
- [ ] Remove from `skills.installed` array if skill was removed
|
|
25
|
+
- [ ] Update any feature flags
|
|
26
|
+
|
|
27
|
+
### 3. Update Documentation
|
|
28
|
+
- [ ] Rename/update doc files in `.claude/docs/`
|
|
29
|
+
- [ ] Update command references in `commands.md`
|
|
30
|
+
- [ ] Update skill-matching.md if skill changed
|
|
31
|
+
- [ ] Search for old name in all `.md` files
|
|
32
|
+
|
|
33
|
+
### 4. Clean References
|
|
34
|
+
- [ ] Search codebase: `grep -r "old-feature-name" .`
|
|
35
|
+
- [ ] Update imports in dependent scripts
|
|
36
|
+
- [ ] Update any hardcoded references
|
|
37
|
+
|
|
38
|
+
### 5. Update State Files
|
|
39
|
+
- [ ] Clean `.workflow/state/` of old state files
|
|
40
|
+
- [ ] Update `ready.json` if tasks reference old feature
|
|
41
|
+
- [ ] Archive old change specs
|
|
42
|
+
|
|
43
|
+
## Search Commands
|
|
44
|
+
|
|
45
|
+
Run these to find lingering references:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Find all references to old feature
|
|
49
|
+
grep -r "old-feature-name" --include="*.js" --include="*.md" --include="*.json" .
|
|
50
|
+
|
|
51
|
+
# Find in config
|
|
52
|
+
grep "oldFeatureName" .workflow/config.json
|
|
53
|
+
|
|
54
|
+
# Find skill references
|
|
55
|
+
grep -r "old-feature" .claude/
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Why This Matters
|
|
59
|
+
|
|
60
|
+
Incomplete cleanup causes:
|
|
61
|
+
- **Confusion**: Old commands/skills appear to work but don't
|
|
62
|
+
- **Bloat**: Dead code accumulates
|
|
63
|
+
- **Errors**: Old references cause runtime failures
|
|
64
|
+
- **Documentation drift**: Docs describe non-existent features
|
|
65
|
+
|
|
66
|
+
## Example: transcript-digestion → long-input-gate
|
|
67
|
+
|
|
68
|
+
When this refactoring happened without proper cleanup:
|
|
69
|
+
|
|
70
|
+
| Artifact | Status | Should Have Been |
|
|
71
|
+
|----------|--------|------------------|
|
|
72
|
+
| `.claude/skills/transcript-digestion/` | Left behind | Deleted |
|
|
73
|
+
| `config.transcriptDigestion` | Left as-is | Renamed to `longInputGate` |
|
|
74
|
+
| `skills.installed` array | Still listed | Removed |
|
|
75
|
+
| `skill-matching.md` | Old references | Updated |
|
|
76
|
+
| `transcript-digestion.md` doc | Still existed | Renamed/rewritten |
|
|
77
|
+
|
|
78
|
+
## Automation Opportunity
|
|
79
|
+
|
|
80
|
+
Consider adding a `flow refactor-cleanup <old-name> <new-name>` command that:
|
|
81
|
+
1. Searches for all references
|
|
82
|
+
2. Shows what needs updating
|
|
83
|
+
3. Optionally auto-updates simple cases
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
Last updated: 2026-01-14
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
globs: scripts/flow-model*.js
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
description: "Model management architecture - two separate systems for different purposes"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Model Management Architecture
|
|
8
|
+
|
|
9
|
+
**Context**: Phase 1 introduced model registry and stats system alongside existing model-adapter.
|
|
10
|
+
|
|
11
|
+
## Two Model Systems
|
|
12
|
+
|
|
13
|
+
### 1. flow-model-adapter.js - Prompt Adaptation
|
|
14
|
+
|
|
15
|
+
- `getCurrentModel()` returns normalized model name (string)
|
|
16
|
+
- Focus: Per-model prompt adjustments, learning, and corrections
|
|
17
|
+
- Imports: Used by flow-knowledge-router.js
|
|
18
|
+
|
|
19
|
+
### 2. flow-models.js - Registry and Stats
|
|
20
|
+
|
|
21
|
+
- `getCurrentModel()` returns `{name, info, source}` object
|
|
22
|
+
- Focus: Model listing, routing recommendations, cost tracking
|
|
23
|
+
- Standalone CLI commands: `flow models [subcommand]`
|
|
24
|
+
|
|
25
|
+
## Design Decision
|
|
26
|
+
|
|
27
|
+
**Keep them separate** because:
|
|
28
|
+
- Different return types serve different consumers
|
|
29
|
+
- Adapter system needs just the name for pattern matching
|
|
30
|
+
- Registry system needs full model metadata for display/routing
|
|
31
|
+
- Merging would create unnecessary coupling
|
|
32
|
+
|
|
33
|
+
## Future Consideration
|
|
34
|
+
|
|
35
|
+
Could extract shared model detection logic into a common utility if they drift apart, but avoid premature abstraction.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Patterns for modifying WogiFlow itself (scripts, templates, config)"
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
globs: "scripts/**,*.workflow/**,.claude/**,templates/**,agents/**,lib/**"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# WogiFlow Self-Maintenance Patterns
|
|
8
|
+
|
|
9
|
+
When modifying WogiFlow's own code (scripts/, templates/, config, hooks), follow these patterns.
|
|
10
|
+
|
|
11
|
+
## 1. Template-First Changes
|
|
12
|
+
|
|
13
|
+
CLAUDE.md is **generated**, not hand-edited. Changes must go through the template system:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
.workflow/templates/claude-md.hbs # Main template
|
|
17
|
+
.workflow/templates/partials/*.hbs # Partial templates
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
After editing templates, regenerate:
|
|
21
|
+
```bash
|
|
22
|
+
node scripts/flow-bridge.js sync claude-code
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Never edit CLAUDE.md directly** - changes will be overwritten on next sync.
|
|
26
|
+
|
|
27
|
+
## 2. Three-Layer Hook Architecture
|
|
28
|
+
|
|
29
|
+
All hooks follow: Entry → Core → Adapter
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
scripts/hooks/entry/claude-code/<name>.js # CLI-specific entry point
|
|
33
|
+
scripts/hooks/core/<name>.js # CLI-agnostic logic
|
|
34
|
+
scripts/hooks/adapters/claude-code.js # Transform results
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
When adding/modifying hooks:
|
|
38
|
+
- Logic goes in `core/` (not entry)
|
|
39
|
+
- Entry files only parse input and call core
|
|
40
|
+
- Register hook in `.claude/settings.local.json`
|
|
41
|
+
- Add config toggle in `.workflow/config.json` under `hooks.rules`
|
|
42
|
+
|
|
43
|
+
## 3. Config Changes Need Documentation
|
|
44
|
+
|
|
45
|
+
When adding config keys:
|
|
46
|
+
- Use `_comment_<keyName>` for inline documentation of non-obvious settings
|
|
47
|
+
- Update config.schema.json if it exists
|
|
48
|
+
- Ensure `lib/installer.js` handles the new key for fresh installs
|
|
49
|
+
|
|
50
|
+
## 4. State File Templates
|
|
51
|
+
|
|
52
|
+
For files in `.workflow/state/` that target projects need:
|
|
53
|
+
- Create both the file AND a `.template` version
|
|
54
|
+
- Templates go in `.workflow/state/<name>.template` (for init/onboard)
|
|
55
|
+
- Also add to `templates/` directory (for npm distribution)
|
|
56
|
+
|
|
57
|
+
## 5. Slash Commands Are Flat Files
|
|
58
|
+
|
|
59
|
+
Slash commands in `.claude/commands/` must be flat `.md` files:
|
|
60
|
+
```
|
|
61
|
+
.claude/commands/wogi-start.md ← Correct (flat file)
|
|
62
|
+
.claude/commands/wogi-start/ ← Wrong (directory)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 6. Two Agent Directories
|
|
66
|
+
|
|
67
|
+
| Directory | Purpose | Used By |
|
|
68
|
+
|-----------|---------|---------|
|
|
69
|
+
| `agents/` | 11 persona files | Health checks, CLI |
|
|
70
|
+
| `.workflow/agents/` | Review checklists | wogi-review |
|
|
71
|
+
|
|
72
|
+
Don't confuse them. `agents/security.md` (persona) is different from `.workflow/agents/security.md` (OWASP checklist).
|
|
73
|
+
|
|
74
|
+
## 7. Regression Prevention
|
|
75
|
+
|
|
76
|
+
When modifying flow-*.js scripts:
|
|
77
|
+
- Run `node --check scripts/<file>.js` after edits
|
|
78
|
+
- WogiFlow has no test suite - syntax checking is the safety net
|
|
79
|
+
- Check for circular dependencies when moving shared functions
|
|
80
|
+
|
|
81
|
+
## 8. Feature Refactoring Cleanup
|
|
82
|
+
|
|
83
|
+
When renaming/replacing a feature, follow the full checklist in `.claude/rules/architecture/feature-refactoring-cleanup.md`. Key steps:
|
|
84
|
+
- Remove old script files
|
|
85
|
+
- Update config keys
|
|
86
|
+
- Update documentation references
|
|
87
|
+
- Search all `.md` files for old name
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
alwaysApply: true
|
|
3
|
+
description: "Naming conventions for files and code variants"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Naming Conventions
|
|
7
|
+
|
|
8
|
+
## File Names
|
|
9
|
+
|
|
10
|
+
Use **kebab-case** for all file names in this project.
|
|
11
|
+
|
|
12
|
+
Examples:
|
|
13
|
+
- `flow-health.js` (correct)
|
|
14
|
+
- `flowHealth.js` (incorrect)
|
|
15
|
+
- `flow_health.js` (incorrect)
|
|
16
|
+
|
|
17
|
+
## Variant Names
|
|
18
|
+
|
|
19
|
+
Use consistent variant names for components:
|
|
20
|
+
|
|
21
|
+
| Category | Values |
|
|
22
|
+
|----------|--------|
|
|
23
|
+
| Size | `sm`, `md`, `lg`, `xl` |
|
|
24
|
+
| Intent | `primary`, `secondary`, `danger`, `success`, `warning` |
|
|
25
|
+
| State | `default`, `hover`, `active`, `disabled` |
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
```jsx
|
|
29
|
+
<Button size="sm" intent="primary" />
|
|
30
|
+
<Badge variant="warning" />
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Catch Block Variables
|
|
34
|
+
|
|
35
|
+
Use `err` for all catch blocks in this codebase.
|
|
36
|
+
|
|
37
|
+
**Avoid**: `e`, `error`, `ex`, `exception` - these cause confusion with loop variables.
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
// Good
|
|
41
|
+
try {
|
|
42
|
+
doSomething();
|
|
43
|
+
} catch (err) {
|
|
44
|
+
console.error(err.message);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Bad - 'e' conflicts with common iterator variables
|
|
48
|
+
try {
|
|
49
|
+
items.map(e => e.value); // 'e' used as iterator
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.error(e.message); // Easy to confuse with iterator 'e'
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Reason**: Standardizing on `err` prevents mix-ups when `.map(e => ...)` is used nearby.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
---
|
|
2
|
+
alwaysApply: true
|
|
3
|
+
description: "Security patterns for file operations, JSON parsing, and path handling"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Security Patterns
|
|
7
|
+
|
|
8
|
+
Critical security patterns for this project.
|
|
9
|
+
|
|
10
|
+
## 1. File Read Safety
|
|
11
|
+
|
|
12
|
+
Always wrap `fs.readFileSync()` in try-catch, even after `fileExists()` check.
|
|
13
|
+
|
|
14
|
+
**Reason**: Race conditions, permission changes, symlink issues can still cause failures.
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
// Good
|
|
18
|
+
try {
|
|
19
|
+
const content = fs.readFileSync(path, 'utf-8');
|
|
20
|
+
} catch (err) {
|
|
21
|
+
// Handle gracefully
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Bad - can still throw even if file existed
|
|
25
|
+
if (fs.existsSync(path)) {
|
|
26
|
+
const content = fs.readFileSync(path, 'utf-8');
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 2. JSON Parsing Safety
|
|
31
|
+
|
|
32
|
+
Use `safeJsonParse()` from flow-utils.js instead of raw `JSON.parse()`.
|
|
33
|
+
|
|
34
|
+
- Check for `__proto__`, `constructor`, `prototype` injection
|
|
35
|
+
- Validate parsed structure has expected fields before use
|
|
36
|
+
- Located in: `scripts/flow-utils.js`
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
// Good
|
|
40
|
+
const config = safeJsonParse(filePath, {});
|
|
41
|
+
|
|
42
|
+
// Bad - vulnerable to prototype pollution
|
|
43
|
+
const config = JSON.parse(fs.readFileSync(filePath));
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 3. Template Substitution Safety
|
|
47
|
+
|
|
48
|
+
When implementing template substitution:
|
|
49
|
+
- Block access to `__proto__`, `constructor`, `prototype` keys
|
|
50
|
+
- Use `Object.prototype.hasOwnProperty.call()` for property access
|
|
51
|
+
- Example: See `applyTemplate()` in flow-prompt-composer.js
|
|
52
|
+
|
|
53
|
+
## 4. Path Safety
|
|
54
|
+
|
|
55
|
+
- Validate patterns before `path.join()` with user/config data
|
|
56
|
+
- Use `isPathWithinProject()` for defense-in-depth
|
|
57
|
+
- Glob-to-regex: Use `[^/]*` not `.*` to prevent path separator matching
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
// Good
|
|
61
|
+
if (!isPathWithinProject(targetPath)) {
|
|
62
|
+
throw new Error('Path outside project');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Bad - allows path traversal
|
|
66
|
+
const fullPath = path.join(baseDir, userInput);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 5. Module Dependencies
|
|
70
|
+
|
|
71
|
+
- Check for circular dependencies when refactoring shared functions
|
|
72
|
+
- Node.js handles circular deps but can cause undefined exports during load
|
|
73
|
+
|
|
74
|
+
## 6. Claude Code Permission Patterns (2.1.7+)
|
|
75
|
+
|
|
76
|
+
When configuring permission rules in Claude Code, avoid overly permissive wildcards.
|
|
77
|
+
|
|
78
|
+
**Vulnerability fixed in 2.1.7**: Wildcard permission rules could match compound commands containing shell operators (`;`, `&&`, `||`, `|`).
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
// DANGEROUS - could match "npm test && rm -rf /"
|
|
82
|
+
"allow": "npm *"
|
|
83
|
+
|
|
84
|
+
// SAFER - be specific about allowed commands
|
|
85
|
+
"allow": "npm test"
|
|
86
|
+
"allow": "npm run build"
|
|
87
|
+
"allow": "npm install"
|
|
88
|
+
|
|
89
|
+
// BEST - use semantic prompts instead of wildcards
|
|
90
|
+
// In ExitPlanMode allowedPrompts:
|
|
91
|
+
{ "tool": "Bash", "prompt": "run tests" }
|
|
92
|
+
{ "tool": "Bash", "prompt": "install dependencies" }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Best practices:**
|
|
96
|
+
- Avoid `*` wildcards in permission rules
|
|
97
|
+
- Use specific command patterns
|
|
98
|
+
- Prefer semantic permission prompts over literal command matching
|
|
99
|
+
- Never allow broad patterns like `rm *` or `git *`
|
|
100
|
+
- Review permission rules after Claude Code updates
|
|
101
|
+
|
|
102
|
+
## 7. Windows Path Safety
|
|
103
|
+
|
|
104
|
+
On Windows, be aware of path-related issues:
|
|
105
|
+
|
|
106
|
+
- Temp directory paths may contain characters like `\t` or `\n` that could be misinterpreted as escape sequences
|
|
107
|
+
- Use raw strings or proper escaping when constructing paths
|
|
108
|
+
- Cloud sync tools (OneDrive, Dropbox) and antivirus may touch file timestamps without changing content
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
// Good - use path.join() which handles platform differences
|
|
112
|
+
const tempPath = path.join(os.tmpdir(), 'myfile.txt');
|
|
113
|
+
|
|
114
|
+
// Bad - manual concatenation can break on Windows
|
|
115
|
+
const tempPath = os.tmpdir() + '/myfile.txt';
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## 8. Shell Command Parameter Validation
|
|
119
|
+
|
|
120
|
+
When executing shell commands with dynamic parameters, always validate inputs.
|
|
121
|
+
|
|
122
|
+
**Risk**: Command injection via unvalidated parameters passed to execSync/spawn.
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
// DANGEROUS - lang parameter not validated
|
|
126
|
+
execSync(`sg --pattern "${pattern}" --lang ${lang} --json "${path}"`);
|
|
127
|
+
|
|
128
|
+
// SAFER - validate against whitelist
|
|
129
|
+
const ALLOWED_LANGUAGES = new Set(['typescript', 'javascript', 'python', 'go']);
|
|
130
|
+
if (!ALLOWED_LANGUAGES.has(lang)) {
|
|
131
|
+
throw new Error(`Unsupported language: ${lang}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// BEST - use execFile with array arguments (no shell interpretation)
|
|
135
|
+
const { execFileSync } = require('child_process');
|
|
136
|
+
execFileSync('sg', ['--pattern', pattern, '--lang', lang, '--json', path]);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Best practices:**
|
|
140
|
+
- Validate all dynamic parameters against allowlists
|
|
141
|
+
- Prefer `execFile`/`execFileSync` with array arguments over `exec`/`execSync` with template strings
|
|
142
|
+
- When using template strings, escape all user-controlled values
|
|
143
|
+
- Never interpolate user input directly into shell commands
|
|
144
|
+
|
|
145
|
+
## 9. Temp Directory Isolation (Claude Code 2.1.23+)
|
|
146
|
+
|
|
147
|
+
On shared systems (CI servers, multi-user machines), use per-user temp directories to prevent permission conflicts.
|
|
148
|
+
|
|
149
|
+
**Fixed in Claude Code 2.1.23**: Per-user temp directory isolation prevents permission conflicts.
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
// Good - per-user isolation
|
|
153
|
+
const userId = process.getuid?.() ?? process.env.USER ?? process.env.USERNAME ?? 'default';
|
|
154
|
+
const tempDir = path.join(os.tmpdir(), `myapp-${userId}`);
|
|
155
|
+
|
|
156
|
+
// Bad - global temp path on shared systems
|
|
157
|
+
const tempDir = path.join(os.tmpdir(), 'myapp');
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Best practices:**
|
|
161
|
+
- Use UID on Unix systems (`process.getuid()`)
|
|
162
|
+
- Fall back to username environment variables on Windows
|
|
163
|
+
- Always provide a 'default' fallback for edge cases
|
|
164
|
+
- This pattern is used in `flow-worktree.js` for worktree isolation
|
|
165
|
+
|
|
166
|
+
## 10. Search/Grep Timeout Handling (Claude Code 2.1.23+)
|
|
167
|
+
|
|
168
|
+
**Fixed in Claude Code 2.1.23**: Ripgrep search timeouts now report errors instead of silently returning empty results.
|
|
169
|
+
|
|
170
|
+
**Impact on WogiFlow:** Component detection, auto-context loading, and pattern matching rely on search operations. Before 2.1.23, search timeouts could cause false negatives.
|
|
171
|
+
|
|
172
|
+
**Best practices:**
|
|
173
|
+
- Handle empty search results gracefully - they may indicate timeout
|
|
174
|
+
- Add retry logic for search-dependent operations
|
|
175
|
+
- Log warnings when searches return unexpectedly empty
|
|
176
|
+
- Consider fallback strategies (glob-based search if grep fails)
|
package/.claude/settings.json
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"PreToolUse": [
|
|
37
37
|
{
|
|
38
|
-
"matcher": "Edit|Write|TodoWrite|Skill|Bash",
|
|
38
|
+
"matcher": "Edit|Write|TodoWrite|Skill|Bash|EnterPlanMode",
|
|
39
39
|
"hooks": [
|
|
40
40
|
{
|
|
41
41
|
"type": "command",
|
|
@@ -115,6 +115,6 @@
|
|
|
115
115
|
]
|
|
116
116
|
},
|
|
117
117
|
"_wogiFlowManaged": true,
|
|
118
|
-
"_wogiFlowVersion": "1.
|
|
118
|
+
"_wogiFlowVersion": "1.5.7",
|
|
119
119
|
"_comment": "Shared WogiFlow hook configuration. Committed to repo for team use. User-specific overrides go in settings.local.json."
|
|
120
120
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Performance Review Agent
|
|
2
|
+
|
|
3
|
+
Expert agent for identifying performance issues in code changes.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
Detect performance anti-patterns, inefficient algorithms, and resource management issues.
|
|
8
|
+
|
|
9
|
+
## Performance Checklist
|
|
10
|
+
|
|
11
|
+
### Async & Concurrency
|
|
12
|
+
- [ ] No sequential awaits that could be `Promise.all`
|
|
13
|
+
- [ ] No blocking I/O in async contexts
|
|
14
|
+
- [ ] No unhandled promise rejections
|
|
15
|
+
- [ ] Async iterators used efficiently
|
|
16
|
+
|
|
17
|
+
### Memory Management
|
|
18
|
+
- [ ] Event listeners cleaned up (removeEventListener, unsubscribe)
|
|
19
|
+
- [ ] Large objects released when no longer needed
|
|
20
|
+
- [ ] No closures capturing unnecessary scope
|
|
21
|
+
- [ ] Streams used for large data instead of loading into memory
|
|
22
|
+
|
|
23
|
+
### Data Access Patterns
|
|
24
|
+
- [ ] No N+1 query patterns (loop with individual DB/API calls)
|
|
25
|
+
- [ ] Batch operations used where available
|
|
26
|
+
- [ ] Results cached when accessed multiple times
|
|
27
|
+
- [ ] Pagination used for large result sets
|
|
28
|
+
|
|
29
|
+
### Bundle & Import Efficiency
|
|
30
|
+
- [ ] No large library imports when small utility suffices
|
|
31
|
+
- [ ] Dynamic imports for code-split boundaries
|
|
32
|
+
- [ ] Tree-shakeable imports (named vs default)
|
|
33
|
+
- [ ] No duplicate dependencies
|
|
34
|
+
|
|
35
|
+
### Computation
|
|
36
|
+
- [ ] No unnecessary re-computation (memoize expensive operations)
|
|
37
|
+
- [ ] Appropriate data structures (Map/Set vs Array for lookups)
|
|
38
|
+
- [ ] Early returns to avoid unnecessary work
|
|
39
|
+
- [ ] No redundant iterations (filter+map that could be reduce)
|
|
40
|
+
|
|
41
|
+
### React-Specific (skip if project does not use React — check package.json for "react" dependency)
|
|
42
|
+
- [ ] Components memoized where appropriate (React.memo, useMemo, useCallback)
|
|
43
|
+
- [ ] No inline object/array creation in render causing re-renders
|
|
44
|
+
- [ ] Keys are stable and meaningful (not array index for dynamic lists)
|
|
45
|
+
- [ ] useEffect dependencies are correct (no missing deps, no over-triggering)
|
|
46
|
+
|
|
47
|
+
## Common Patterns to Flag
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
// BAD: Sequential awaits (N+1 pattern)
|
|
51
|
+
for (const id of ids) {
|
|
52
|
+
const result = await fetchItem(id);
|
|
53
|
+
results.push(result);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// GOOD: Parallel execution
|
|
57
|
+
const results = await Promise.all(ids.map(id => fetchItem(id)));
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
// BAD: Event listener leak
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
window.addEventListener('resize', handler);
|
|
64
|
+
// Missing cleanup!
|
|
65
|
+
}, []);
|
|
66
|
+
|
|
67
|
+
// GOOD: Cleanup on unmount
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
window.addEventListener('resize', handler);
|
|
70
|
+
return () => window.removeEventListener('resize', handler);
|
|
71
|
+
}, []);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
// BAD: Large library for small task
|
|
76
|
+
import _ from 'lodash';
|
|
77
|
+
const unique = _.uniq(items);
|
|
78
|
+
|
|
79
|
+
// GOOD: Use native or targeted import
|
|
80
|
+
const unique = [...new Set(items)];
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Severity Ratings
|
|
84
|
+
|
|
85
|
+
| Severity | Description | Example |
|
|
86
|
+
|----------|-------------|---------|
|
|
87
|
+
| Critical | Major performance impact at scale | N+1 queries in API endpoint |
|
|
88
|
+
| High | Noticeable performance impact | Memory leak in long-running component |
|
|
89
|
+
| Medium | Suboptimal but functional | Sequential awaits on 2-3 items |
|
|
90
|
+
| Low | Micro-optimization | filter+map vs reduce |
|
|
91
|
+
|
|
92
|
+
## Review Format
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
## File: [path]
|
|
96
|
+
|
|
97
|
+
### Line [N]: [severity] Performance
|
|
98
|
+
Description of the performance issue.
|
|
99
|
+
|
|
100
|
+
**Impact**: [What gets slower/uses more resources]
|
|
101
|
+
**Pattern**: [Name of the anti-pattern]
|
|
102
|
+
|
|
103
|
+
**Current:**
|
|
104
|
+
\`\`\`
|
|
105
|
+
[current code]
|
|
106
|
+
\`\`\`
|
|
107
|
+
|
|
108
|
+
**Suggested:**
|
|
109
|
+
\`\`\`
|
|
110
|
+
[optimized code]
|
|
111
|
+
\`\`\`
|
|
112
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Pattern
|
|
4
|
+
|
|
5
|
+
<!-- Detected during onboarding -->
|
|
6
|
+
<!-- Examples: MVC, Clean Architecture, DDD, Microservices, Monolith -->
|
|
7
|
+
|
|
8
|
+
## Structure
|
|
9
|
+
|
|
10
|
+
<!-- Project structure overview -->
|
|
11
|
+
<!-- Key directories and their purposes -->
|
|
12
|
+
|
|
13
|
+
## Key Decisions
|
|
14
|
+
|
|
15
|
+
<!-- Architecture decisions made for this project -->
|
|
16
|
+
<!-- Trade-offs and rationale -->
|
|
17
|
+
|
|
18
|
+
## Dependencies
|
|
19
|
+
|
|
20
|
+
<!-- Major dependencies and their purposes -->
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
*This file is auto-populated during `flow onboard`. Update manually as architecture evolves.*
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Tech Stack
|
|
2
|
+
|
|
3
|
+
## Framework
|
|
4
|
+
|
|
5
|
+
<!-- Primary framework (e.g., Next.js, NestJS, FastAPI) -->
|
|
6
|
+
|
|
7
|
+
## Language
|
|
8
|
+
|
|
9
|
+
<!-- Primary language (e.g., TypeScript, Python, Go) -->
|
|
10
|
+
|
|
11
|
+
## Database
|
|
12
|
+
|
|
13
|
+
<!-- Database system if applicable (e.g., PostgreSQL, MongoDB) -->
|
|
14
|
+
|
|
15
|
+
## Package Manager
|
|
16
|
+
|
|
17
|
+
<!-- npm, yarn, pnpm, pip, etc. -->
|
|
18
|
+
|
|
19
|
+
## Build Tools
|
|
20
|
+
|
|
21
|
+
<!-- Build and bundling tools (e.g., Vite, Webpack, esbuild) -->
|
|
22
|
+
|
|
23
|
+
## Testing
|
|
24
|
+
|
|
25
|
+
<!-- Test frameworks (e.g., Jest, Vitest, pytest) -->
|
|
26
|
+
|
|
27
|
+
## Other Tools
|
|
28
|
+
|
|
29
|
+
<!-- Linters, formatters, CI/CD tools -->
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
*This file is auto-populated during `flow onboard`. Update manually as stack evolves.*
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
## Test Framework
|
|
4
|
+
|
|
5
|
+
<!-- Primary test framework (e.g., Jest, Vitest, pytest) -->
|
|
6
|
+
|
|
7
|
+
## Test Commands
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Run all tests
|
|
11
|
+
npm test
|
|
12
|
+
|
|
13
|
+
# Run tests in watch mode
|
|
14
|
+
npm run test:watch
|
|
15
|
+
|
|
16
|
+
# Run specific test file
|
|
17
|
+
npm test -- path/to/test.ts
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Test Structure
|
|
21
|
+
|
|
22
|
+
<!-- Where tests are located -->
|
|
23
|
+
<!-- Naming conventions -->
|
|
24
|
+
|
|
25
|
+
## Coverage
|
|
26
|
+
|
|
27
|
+
<!-- Coverage requirements if any -->
|
|
28
|
+
<!-- Coverage commands -->
|
|
29
|
+
|
|
30
|
+
## E2E Testing
|
|
31
|
+
|
|
32
|
+
<!-- E2E framework if used (e.g., Playwright, Cypress) -->
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
*This file is auto-populated during `flow onboard`. Update manually as testing strategy evolves.*
|
package/package.json
CHANGED
|
@@ -18,7 +18,10 @@ const path = require('path');
|
|
|
18
18
|
|
|
19
19
|
const { getConfig, getReadyData, PATHS } = require('../../flow-utils');
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
// Include session ID in flag path to prevent concurrent sessions from
|
|
22
|
+
// interfering with each other. Falls back to PID-based path if no session ID.
|
|
23
|
+
const SESSION_ID = process.env.CLAUDE_CODE_SESSION_ID || `pid-${process.ppid || process.pid}`;
|
|
24
|
+
const ROUTING_FLAG_PATH = path.join(PATHS.state, `.routing-pending-${SESSION_ID}`);
|
|
22
25
|
|
|
23
26
|
/**
|
|
24
27
|
* Check if routing gate is enabled in config
|
|
@@ -32,8 +35,10 @@ function isRoutingGateEnabled() {
|
|
|
32
35
|
if (process.env.DEBUG) {
|
|
33
36
|
console.error(`[routing-gate] Config read error: ${err.message}`);
|
|
34
37
|
}
|
|
35
|
-
// Fail-
|
|
36
|
-
|
|
38
|
+
// Fail-closed: if config can't be read, enforce the gate.
|
|
39
|
+
// Users who installed WogiFlow expect routing enforcement.
|
|
40
|
+
// Failing open here would silently bypass routing on config corruption.
|
|
41
|
+
return true;
|
|
37
42
|
}
|
|
38
43
|
}
|
|
39
44
|
|
|
@@ -118,8 +123,10 @@ function clearRoutingPending() {
|
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
|
|
121
|
-
// Max age for routing flag before it's considered stale (
|
|
122
|
-
|
|
126
|
+
// Max age for routing flag before it's considered stale (30 minutes)
|
|
127
|
+
// 5 min was too short — complex tasks with explore phases, spec generation,
|
|
128
|
+
// and approval gates can take 15-20 min before first Bash call.
|
|
129
|
+
const ROUTING_FLAG_TTL_MS = 30 * 60 * 1000;
|
|
123
130
|
|
|
124
131
|
/**
|
|
125
132
|
* Check if the routing-pending flag is set and not stale
|
|
@@ -123,7 +123,9 @@ async function main() {
|
|
|
123
123
|
// When users type "/wogi-start ..." directly, Claude Code expands the skill inline
|
|
124
124
|
// (not through the Skill tool), so clearRoutingPending() in PreToolUse never fires.
|
|
125
125
|
// Setting the flag here would create an uncleable block.
|
|
126
|
-
|
|
126
|
+
// Tightened regex: only match /wogi-[lowercase-alphanumeric-hyphens] to prevent
|
|
127
|
+
// injection via crafted prompts like "/wogi-<script>" or "/wogi-../../path"
|
|
128
|
+
const isWogiCommand = typeof prompt === 'string' && /^\/wogi-[a-z0-9-]+\b/i.test(prompt.trim());
|
|
127
129
|
if (!isWogiCommand) {
|
|
128
130
|
try {
|
|
129
131
|
setRoutingPending();
|
package/scripts/postinstall.js
CHANGED
|
@@ -30,13 +30,42 @@ const STATE_DIR = path.join(WORKFLOW_DIR, 'state');
|
|
|
30
30
|
const DIR_MODE = 0o755; // rwxr-xr-x for directories
|
|
31
31
|
const FILE_MODE = 0o644; // rw-r--r-- for files
|
|
32
32
|
|
|
33
|
+
// Dangerous keys for prototype pollution protection
|
|
34
|
+
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Safe JSON parse with prototype pollution protection.
|
|
38
|
+
* Inline copy — postinstall.js can't reliably require flow-utils.js
|
|
39
|
+
* because it runs from npm context before scripts/ is fully copied.
|
|
40
|
+
* @param {string} jsonString - JSON string to parse
|
|
41
|
+
* @param {*} defaultValue - Default value on parse failure
|
|
42
|
+
* @returns {Object} Parsed object or defaultValue
|
|
43
|
+
*/
|
|
44
|
+
function safeJsonParseString(jsonString, defaultValue = null) {
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(jsonString);
|
|
47
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
48
|
+
return defaultValue;
|
|
49
|
+
}
|
|
50
|
+
// Check top-level keys for prototype pollution
|
|
51
|
+
for (const key of Object.keys(parsed)) {
|
|
52
|
+
if (DANGEROUS_KEYS.has(key)) {
|
|
53
|
+
return defaultValue;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return parsed;
|
|
57
|
+
} catch (err) {
|
|
58
|
+
return defaultValue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
33
62
|
/**
|
|
34
63
|
* Safely close a file descriptor, ignoring errors
|
|
35
64
|
* @param {number|null} fd - File descriptor to close
|
|
36
65
|
*/
|
|
37
66
|
function safeClose(fd) {
|
|
38
67
|
if (fd !== null) {
|
|
39
|
-
try { fs.closeSync(fd); } catch (
|
|
68
|
+
try { fs.closeSync(fd); } catch (err) { /* intentionally ignored */ }
|
|
40
69
|
}
|
|
41
70
|
}
|
|
42
71
|
|
|
@@ -195,11 +224,9 @@ function copyClaudeResources() {
|
|
|
195
224
|
if (fs.existsSync(projectSettings)) {
|
|
196
225
|
// Always merge hooks from package into existing settings
|
|
197
226
|
try {
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
const existing = (existingRaw && typeof existingRaw === 'object' && !Array.isArray(existingRaw)) ? existingRaw : {};
|
|
202
|
-
const ours = (oursRaw && typeof oursRaw === 'object' && !Array.isArray(oursRaw)) ? oursRaw : {};
|
|
227
|
+
// Use safeJsonParseString for prototype pollution protection
|
|
228
|
+
const existing = safeJsonParseString(fs.readFileSync(projectSettings, 'utf-8'), {});
|
|
229
|
+
const ours = safeJsonParseString(fs.readFileSync(packageSettings, 'utf-8'), {});
|
|
203
230
|
// Always update hooks (core WogiFlow functionality)
|
|
204
231
|
existing.hooks = ours.hooks;
|
|
205
232
|
existing._wogiFlowManaged = true;
|
|
@@ -224,7 +251,7 @@ function copyClaudeResources() {
|
|
|
224
251
|
fs.copyFileSync(packageSettings, projectSettings);
|
|
225
252
|
try {
|
|
226
253
|
fs.chmodSync(projectSettings, FILE_MODE);
|
|
227
|
-
} catch (
|
|
254
|
+
} catch (err) { /* non-critical */ }
|
|
228
255
|
} catch (err) {
|
|
229
256
|
if (process.env.DEBUG) {
|
|
230
257
|
console.error(`[postinstall] settings.json initial copy failed: ${err.message}`);
|
|
@@ -288,6 +315,45 @@ function copyWorkflowManagedDirs() {
|
|
|
288
315
|
}
|
|
289
316
|
}
|
|
290
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Regenerate CLAUDE.md from templates (for npm update scenario)
|
|
320
|
+
* Only runs when config.json exists (project already initialized).
|
|
321
|
+
* Uses the bridge's synchronous generateRulesFile() to avoid async in postinstall.
|
|
322
|
+
*/
|
|
323
|
+
function regenerateClaudeMd() {
|
|
324
|
+
// Only regenerate if project is already initialized (has config.json)
|
|
325
|
+
if (!fs.existsSync(path.join(WORKFLOW_DIR, 'config.json'))) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
try {
|
|
330
|
+
// Load the bridge module from the project's .workflow/bridges/
|
|
331
|
+
const bridgesPath = path.join(PROJECT_ROOT, '.workflow', 'bridges');
|
|
332
|
+
if (!fs.existsSync(path.join(bridgesPath, 'index.js'))) {
|
|
333
|
+
if (process.env.DEBUG) {
|
|
334
|
+
console.error('[postinstall] Bridge module not found, skipping CLAUDE.md regen');
|
|
335
|
+
}
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const { getBridge } = require(bridgesPath);
|
|
340
|
+
const bridge = getBridge({ projectDir: PROJECT_ROOT });
|
|
341
|
+
|
|
342
|
+
// generateRulesFile() is synchronous — safe to call from postinstall
|
|
343
|
+
// force: true ensures templates always win over stale CLAUDE.md
|
|
344
|
+
bridge.generateRulesFile({ force: true });
|
|
345
|
+
|
|
346
|
+
if (process.env.DEBUG) {
|
|
347
|
+
console.error('[postinstall] Regenerated CLAUDE.md from templates');
|
|
348
|
+
}
|
|
349
|
+
} catch (err) {
|
|
350
|
+
// Non-fatal — CLAUDE.md regeneration failure shouldn't break npm install
|
|
351
|
+
if (process.env.DEBUG) {
|
|
352
|
+
console.error(`[postinstall] CLAUDE.md regen failed: ${err.message}`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
291
357
|
/**
|
|
292
358
|
* Check if we should be completely silent (CI only)
|
|
293
359
|
*/
|
|
@@ -324,6 +390,11 @@ function main() {
|
|
|
324
390
|
// Always overwrite — these are package-managed, not user-customizable.
|
|
325
391
|
copyWorkflowManagedDirs();
|
|
326
392
|
|
|
393
|
+
// Regenerate CLAUDE.md from updated templates (for npm update scenario)
|
|
394
|
+
// This ensures the AI reads fresh instructions matching the new package version.
|
|
395
|
+
// Must run AFTER copyWorkflowManagedDirs() so templates/bridges are up to date.
|
|
396
|
+
regenerateClaudeMd();
|
|
397
|
+
|
|
327
398
|
// Create marker for AI to detect (unless already initialized)
|
|
328
399
|
createPendingSetupMarker();
|
|
329
400
|
|
|
@@ -343,7 +414,7 @@ function main() {
|
|
|
343
414
|
// Combine access check and open into single try-catch to avoid TOCTOU
|
|
344
415
|
ttyFd = fs.openSync('/dev/tty', 'w');
|
|
345
416
|
output = { write: (msg) => fs.writeSync(ttyFd, msg) };
|
|
346
|
-
} catch (
|
|
417
|
+
} catch (err) {
|
|
347
418
|
// /dev/tty not available (no terminal, CI, etc.) - fallback to stderr
|
|
348
419
|
ttyFd = null;
|
|
349
420
|
}
|