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,81 @@
|
|
|
1
|
+
# Code Review Agent
|
|
2
|
+
|
|
3
|
+
Expert agent for conducting thorough code reviews.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
Review code changes for quality, correctness, and maintainability.
|
|
8
|
+
|
|
9
|
+
## Review Checklist
|
|
10
|
+
|
|
11
|
+
### Code Quality
|
|
12
|
+
- [ ] Naming is clear and consistent
|
|
13
|
+
- [ ] Functions are small and focused
|
|
14
|
+
- [ ] No dead code or unused imports
|
|
15
|
+
- [ ] Comments explain "why", not "what"
|
|
16
|
+
- [ ] Consistent formatting
|
|
17
|
+
|
|
18
|
+
### Logic & Correctness
|
|
19
|
+
- [ ] Algorithm is correct
|
|
20
|
+
- [ ] Edge cases handled
|
|
21
|
+
- [ ] Error states managed
|
|
22
|
+
- [ ] No off-by-one errors
|
|
23
|
+
- [ ] Null/undefined checks where needed
|
|
24
|
+
|
|
25
|
+
### Performance
|
|
26
|
+
- [ ] No unnecessary loops/iterations
|
|
27
|
+
- [ ] Large data sets handled efficiently
|
|
28
|
+
- [ ] No memory leaks
|
|
29
|
+
- [ ] Async operations handled correctly
|
|
30
|
+
|
|
31
|
+
### Testing
|
|
32
|
+
- [ ] Tests cover happy path
|
|
33
|
+
- [ ] Tests cover error cases
|
|
34
|
+
- [ ] Tests are readable
|
|
35
|
+
- [ ] No flaky tests
|
|
36
|
+
|
|
37
|
+
### Architecture
|
|
38
|
+
- [ ] Follows existing patterns (check decisions.md)
|
|
39
|
+
- [ ] Uses existing components (check app-map.md)
|
|
40
|
+
- [ ] Separation of concerns
|
|
41
|
+
- [ ] Dependencies are appropriate
|
|
42
|
+
|
|
43
|
+
## Issue Severity
|
|
44
|
+
|
|
45
|
+
| Severity | Description | Action |
|
|
46
|
+
|----------|-------------|--------|
|
|
47
|
+
| Critical | Bug that breaks functionality | Block merge |
|
|
48
|
+
| High | Security issue or major problem | Block merge |
|
|
49
|
+
| Medium | Could cause issues | Should fix |
|
|
50
|
+
| Low | Stylistic or minor | Nice to have |
|
|
51
|
+
|
|
52
|
+
## Review Format
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
## File: [path]
|
|
56
|
+
|
|
57
|
+
### Line [N]: [severity] [type]
|
|
58
|
+
Description of the issue.
|
|
59
|
+
|
|
60
|
+
**Current:**
|
|
61
|
+
\`\`\`
|
|
62
|
+
[current code]
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
**Suggested:**
|
|
66
|
+
\`\`\`
|
|
67
|
+
[suggested fix]
|
|
68
|
+
\`\`\`
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## When Approving
|
|
72
|
+
|
|
73
|
+
Provide a brief summary:
|
|
74
|
+
```
|
|
75
|
+
✓ Approved
|
|
76
|
+
|
|
77
|
+
Summary:
|
|
78
|
+
- [What the change does]
|
|
79
|
+
- [Key decisions made]
|
|
80
|
+
- [Any follow-up items]
|
|
81
|
+
```
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Security Review Agent
|
|
2
|
+
|
|
3
|
+
Expert agent for reviewing code against OWASP Top 10 and security best practices.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
Identify security vulnerabilities in code changes and recommend fixes.
|
|
8
|
+
|
|
9
|
+
## OWASP Top 10 Checklist
|
|
10
|
+
|
|
11
|
+
### A01: Broken Access Control
|
|
12
|
+
- [ ] Authorization checks on every protected resource
|
|
13
|
+
- [ ] Deny by default for new functionality
|
|
14
|
+
- [ ] Rate limiting on APIs
|
|
15
|
+
- [ ] Invalidate sessions on logout
|
|
16
|
+
|
|
17
|
+
### A02: Cryptographic Failures
|
|
18
|
+
- [ ] Data classified by sensitivity
|
|
19
|
+
- [ ] No sensitive data in URLs
|
|
20
|
+
- [ ] Strong encryption for data at rest
|
|
21
|
+
- [ ] TLS for data in transit
|
|
22
|
+
|
|
23
|
+
### A03: Injection
|
|
24
|
+
- [ ] Parameterized queries for SQL
|
|
25
|
+
- [ ] Input validation on all user data
|
|
26
|
+
- [ ] Output encoding for HTML contexts
|
|
27
|
+
- [ ] Command injection prevention
|
|
28
|
+
|
|
29
|
+
### A04: Insecure Design
|
|
30
|
+
- [ ] Threat modeling for new features
|
|
31
|
+
- [ ] Secure by default configurations
|
|
32
|
+
- [ ] Unit tests for security controls
|
|
33
|
+
|
|
34
|
+
### A05: Security Misconfiguration
|
|
35
|
+
- [ ] No default credentials
|
|
36
|
+
- [ ] Error messages don't leak info
|
|
37
|
+
- [ ] Security headers configured
|
|
38
|
+
- [ ] Unnecessary features disabled
|
|
39
|
+
|
|
40
|
+
### A06: Vulnerable Components
|
|
41
|
+
- [ ] Dependencies audited (`npm audit`)
|
|
42
|
+
- [ ] No known vulnerable versions
|
|
43
|
+
- [ ] Update process documented
|
|
44
|
+
|
|
45
|
+
### A07: Authentication Failures
|
|
46
|
+
- [ ] Strong password requirements
|
|
47
|
+
- [ ] Multi-factor authentication option
|
|
48
|
+
- [ ] Account lockout after failures
|
|
49
|
+
- [ ] Secure session management
|
|
50
|
+
|
|
51
|
+
### A08: Software and Data Integrity Failures
|
|
52
|
+
- [ ] CI/CD pipeline secured
|
|
53
|
+
- [ ] Signed commits/releases
|
|
54
|
+
- [ ] Dependency integrity verification
|
|
55
|
+
|
|
56
|
+
### A09: Security Logging Failures
|
|
57
|
+
- [ ] Login/logout events logged
|
|
58
|
+
- [ ] Access control failures logged
|
|
59
|
+
- [ ] Logs protected from tampering
|
|
60
|
+
- [ ] No sensitive data in logs
|
|
61
|
+
|
|
62
|
+
### A10: Server-Side Request Forgery (SSRF)
|
|
63
|
+
- [ ] URL validation for external calls
|
|
64
|
+
- [ ] Allowlist for external services
|
|
65
|
+
- [ ] Firewall rules for internal networks
|
|
66
|
+
|
|
67
|
+
## Common Patterns to Flag
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
// BAD: Raw JSON parse
|
|
71
|
+
JSON.parse(userInput)
|
|
72
|
+
|
|
73
|
+
// GOOD: Safe JSON parse with try-catch
|
|
74
|
+
try {
|
|
75
|
+
const data = JSON.parse(userInput);
|
|
76
|
+
if (data.__proto__) throw new Error('Invalid');
|
|
77
|
+
} catch { return null; }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
// BAD: Path without validation
|
|
82
|
+
fs.readFileSync(path.join(base, userInput))
|
|
83
|
+
|
|
84
|
+
// GOOD: Validate path is within base
|
|
85
|
+
const resolved = path.resolve(base, userInput);
|
|
86
|
+
if (!resolved.startsWith(base)) throw new Error('Invalid path');
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Severity Ratings
|
|
90
|
+
|
|
91
|
+
- **Critical**: Immediate exploitation risk
|
|
92
|
+
- **High**: Significant security impact
|
|
93
|
+
- **Medium**: Requires specific conditions
|
|
94
|
+
- **Low**: Minimal security impact
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Story Writer Agent
|
|
2
|
+
|
|
3
|
+
Expert agent for creating well-structured user stories with clear acceptance criteria.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
Transform vague feature requests into actionable user stories with:
|
|
8
|
+
- Clear user personas
|
|
9
|
+
- Specific value propositions
|
|
10
|
+
- Testable acceptance criteria using Given/When/Then format
|
|
11
|
+
|
|
12
|
+
## Story Template
|
|
13
|
+
|
|
14
|
+
```markdown
|
|
15
|
+
# [wf-XXXXXXXX] [Title]
|
|
16
|
+
|
|
17
|
+
## User Story
|
|
18
|
+
**As a** [user persona]
|
|
19
|
+
**I want** [feature/action]
|
|
20
|
+
**So that** [benefit/value]
|
|
21
|
+
|
|
22
|
+
## Description
|
|
23
|
+
[2-4 sentences explaining context and scope]
|
|
24
|
+
|
|
25
|
+
## Acceptance Criteria
|
|
26
|
+
|
|
27
|
+
### Scenario 1: [Happy path]
|
|
28
|
+
**Given** [initial context]
|
|
29
|
+
**When** [action is taken]
|
|
30
|
+
**Then** [expected outcome]
|
|
31
|
+
|
|
32
|
+
### Scenario 2: [Error handling]
|
|
33
|
+
**Given** [context]
|
|
34
|
+
**When** [invalid action]
|
|
35
|
+
**Then** [error response]
|
|
36
|
+
|
|
37
|
+
## Technical Notes
|
|
38
|
+
- Components to create/modify
|
|
39
|
+
- API endpoints involved
|
|
40
|
+
- Dependencies
|
|
41
|
+
|
|
42
|
+
## Test Strategy
|
|
43
|
+
- [ ] Unit tests for [component]
|
|
44
|
+
- [ ] Integration test for [flow]
|
|
45
|
+
|
|
46
|
+
## Dependencies
|
|
47
|
+
- [Depends on wf-XXXXXXXX]
|
|
48
|
+
|
|
49
|
+
## Complexity
|
|
50
|
+
Low | Medium | High
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Guidelines
|
|
54
|
+
|
|
55
|
+
1. **Be specific** - Avoid vague acceptance criteria
|
|
56
|
+
2. **Include error states** - What happens when things go wrong?
|
|
57
|
+
3. **Reference existing components** - Check app-map.md first
|
|
58
|
+
4. **Set realistic scope** - If it's too big, decompose into sub-tasks
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Bridge Class
|
|
3
|
+
*
|
|
4
|
+
* Abstract base class for CLI bridges. Each supported CLI (Claude Code, Gemini CLI, etc.)
|
|
5
|
+
* extends this class to generate its specific file structure from the universal .workflow/ config.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
*
|
|
9
|
+
* .workflow/ ← Universal source of truth
|
|
10
|
+
* ├── config.json ← Contains cli.type setting
|
|
11
|
+
* ├── models/ ← Model registry (CLI-agnostic)
|
|
12
|
+
* ├── skills/ ← Skills (CLI-agnostic)
|
|
13
|
+
* └── templates/ ← Templates for generating CLI files
|
|
14
|
+
* │
|
|
15
|
+
* ▼ (bridge generates CLI-specific files)
|
|
16
|
+
* .claude/ or .gemini/ etc. ← Generated by bridge
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
// Import safeJsonParse from flow-utils to avoid duplicate implementation
|
|
23
|
+
let safeJsonParse;
|
|
24
|
+
try {
|
|
25
|
+
safeJsonParse = require('../../scripts/flow-utils').safeJsonParse;
|
|
26
|
+
} catch {
|
|
27
|
+
// Fallback if flow-utils not available
|
|
28
|
+
safeJsonParse = null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class BaseBridge {
|
|
32
|
+
/**
|
|
33
|
+
* @param {string} cliType - The CLI type identifier (e.g., 'claude-code', 'gemini-cli')
|
|
34
|
+
* @param {Object} options - Configuration options
|
|
35
|
+
* @param {string} options.workflowDir - Path to .workflow directory
|
|
36
|
+
* @param {string} options.projectDir - Path to project root
|
|
37
|
+
*/
|
|
38
|
+
constructor(cliType, options = {}) {
|
|
39
|
+
this.cliType = cliType;
|
|
40
|
+
this.workflowDir = options.workflowDir || '.workflow';
|
|
41
|
+
this.projectDir = options.projectDir || process.cwd();
|
|
42
|
+
this.verbose = options.verbose || false;
|
|
43
|
+
|
|
44
|
+
// Note: These properties are deprecated in favor of getter methods below.
|
|
45
|
+
// Kept for backwards compatibility with any subclasses that set them directly.
|
|
46
|
+
// New subclasses should override getCliFolder(), getRulesFileName(), etc.
|
|
47
|
+
this.cliFolder = null;
|
|
48
|
+
this.rulesFile = null;
|
|
49
|
+
this.skillsPath = null;
|
|
50
|
+
this.rulesPath = null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get the CLI-specific folder name
|
|
55
|
+
* @abstract
|
|
56
|
+
* @returns {string}
|
|
57
|
+
*/
|
|
58
|
+
getCliFolder() {
|
|
59
|
+
throw new Error('Subclass must implement getCliFolder()');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get the rules file name (e.g., CLAUDE.md)
|
|
64
|
+
* @abstract
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
getRulesFileName() {
|
|
68
|
+
throw new Error('Subclass must implement getRulesFileName()');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get the path where skills should be synced
|
|
73
|
+
* @abstract
|
|
74
|
+
* @returns {string}
|
|
75
|
+
*/
|
|
76
|
+
getSkillsPath() {
|
|
77
|
+
throw new Error('Subclass must implement getSkillsPath()');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get the path where rules should be synced
|
|
82
|
+
* @abstract
|
|
83
|
+
* @returns {string}
|
|
84
|
+
*/
|
|
85
|
+
getRulesPath() {
|
|
86
|
+
throw new Error('Subclass must implement getRulesPath()');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Generate the main rules/instructions file content
|
|
91
|
+
* @abstract
|
|
92
|
+
* @param {Object} config - The workflow config
|
|
93
|
+
* @returns {string}
|
|
94
|
+
*/
|
|
95
|
+
generateRulesContent(config) {
|
|
96
|
+
throw new Error('Subclass must implement generateRulesContent()');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Perform any CLI-specific setup
|
|
101
|
+
* @abstract
|
|
102
|
+
* @param {Object} config - The workflow config
|
|
103
|
+
*/
|
|
104
|
+
async setupCliSpecific(config) {
|
|
105
|
+
// Default: no-op. Override in subclasses if needed.
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ==================== Common Methods ====================
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Safe JSON parse that checks for prototype pollution (fallback when flow-utils unavailable)
|
|
112
|
+
* @param {string} content - JSON string to parse
|
|
113
|
+
* @returns {Object|null} Parsed object or null if invalid
|
|
114
|
+
*/
|
|
115
|
+
safeJsonParseContent(content) {
|
|
116
|
+
// Check for prototype pollution attempts (case-insensitive)
|
|
117
|
+
const contentLower = content.toLowerCase();
|
|
118
|
+
if (contentLower.includes('__proto__') ||
|
|
119
|
+
contentLower.includes('constructor') ||
|
|
120
|
+
contentLower.includes('prototype')) {
|
|
121
|
+
this.log('Warning: Potential prototype pollution detected in JSON');
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const parsed = JSON.parse(content);
|
|
127
|
+
if (parsed === null || typeof parsed !== 'object') {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
return parsed;
|
|
131
|
+
} catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Read the workflow config
|
|
138
|
+
* @returns {Object}
|
|
139
|
+
*/
|
|
140
|
+
readConfig() {
|
|
141
|
+
const configPath = path.join(this.projectDir, this.workflowDir, 'config.json');
|
|
142
|
+
if (!fs.existsSync(configPath)) {
|
|
143
|
+
throw new Error(`Config not found: ${configPath}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Use flow-utils safeJsonParse if available, otherwise fall back to class method
|
|
147
|
+
let config;
|
|
148
|
+
if (safeJsonParse) {
|
|
149
|
+
config = safeJsonParse(configPath, null);
|
|
150
|
+
} else {
|
|
151
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
152
|
+
config = this.safeJsonParseContent(content);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!config) {
|
|
156
|
+
throw new Error(`Invalid config format: ${configPath}`);
|
|
157
|
+
}
|
|
158
|
+
return config;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Ensure the CLI folder exists
|
|
163
|
+
*/
|
|
164
|
+
ensureCliFolder() {
|
|
165
|
+
const cliFolder = path.join(this.projectDir, this.getCliFolder());
|
|
166
|
+
if (!fs.existsSync(cliFolder)) {
|
|
167
|
+
fs.mkdirSync(cliFolder, { recursive: true });
|
|
168
|
+
this.log(`Created ${this.getCliFolder()}/`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Sync skills from .workflow/skills/ to CLI-specific location
|
|
174
|
+
*/
|
|
175
|
+
syncSkills() {
|
|
176
|
+
const sourceSkillsDir = path.join(this.projectDir, this.workflowDir, 'skills');
|
|
177
|
+
const targetSkillsDir = path.join(this.projectDir, this.getSkillsPath());
|
|
178
|
+
|
|
179
|
+
if (!fs.existsSync(sourceSkillsDir)) {
|
|
180
|
+
this.log('No skills to sync (no .workflow/skills/ directory)');
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Ensure target directory exists
|
|
185
|
+
if (!fs.existsSync(targetSkillsDir)) {
|
|
186
|
+
fs.mkdirSync(targetSkillsDir, { recursive: true });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Copy skills
|
|
190
|
+
const skills = fs.readdirSync(sourceSkillsDir).filter(item => {
|
|
191
|
+
const itemPath = path.join(sourceSkillsDir, item);
|
|
192
|
+
return fs.statSync(itemPath).isDirectory();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
for (const skill of skills) {
|
|
196
|
+
const sourcePath = path.join(sourceSkillsDir, skill);
|
|
197
|
+
const targetPath = path.join(targetSkillsDir, skill);
|
|
198
|
+
|
|
199
|
+
this.copyDirRecursive(sourcePath, targetPath);
|
|
200
|
+
this.log(`Synced skill: ${skill}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Also copy skills-index.json if it exists
|
|
204
|
+
const indexPath = path.join(sourceSkillsDir, 'skills-index.json');
|
|
205
|
+
if (fs.existsSync(indexPath)) {
|
|
206
|
+
fs.copyFileSync(indexPath, path.join(targetSkillsDir, 'skills-index.json'));
|
|
207
|
+
this.log('Synced skills-index.json');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Sync rules from .workflow/state/decisions.md to CLI-specific rules folder
|
|
213
|
+
*/
|
|
214
|
+
syncRules() {
|
|
215
|
+
const rulesDir = path.join(this.projectDir, this.getRulesPath());
|
|
216
|
+
|
|
217
|
+
// Ensure rules directory exists
|
|
218
|
+
if (!fs.existsSync(rulesDir)) {
|
|
219
|
+
fs.mkdirSync(rulesDir, { recursive: true });
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Copy any existing rules from .workflow/rules/ if present
|
|
223
|
+
const sourceRulesDir = path.join(this.projectDir, this.workflowDir, 'rules');
|
|
224
|
+
if (fs.existsSync(sourceRulesDir)) {
|
|
225
|
+
const rules = fs.readdirSync(sourceRulesDir).filter(f => f.endsWith('.md'));
|
|
226
|
+
for (const rule of rules) {
|
|
227
|
+
fs.copyFileSync(
|
|
228
|
+
path.join(sourceRulesDir, rule),
|
|
229
|
+
path.join(rulesDir, rule)
|
|
230
|
+
);
|
|
231
|
+
this.log(`Synced rule: ${rule}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Sync knowledge files from .workflow/state/ to CLI-specific docs folder
|
|
238
|
+
* Also updates the knowledge-sync.json state
|
|
239
|
+
*/
|
|
240
|
+
syncKnowledgeFiles() {
|
|
241
|
+
const stateDir = path.join(this.projectDir, this.workflowDir, 'state');
|
|
242
|
+
const cliDocsDir = path.join(this.projectDir, this.getCliFolder(), 'docs');
|
|
243
|
+
|
|
244
|
+
// Knowledge files to sync
|
|
245
|
+
const knowledgeFiles = ['stack.md', 'architecture.md', 'testing.md'];
|
|
246
|
+
|
|
247
|
+
// Ensure CLI docs directory exists
|
|
248
|
+
if (!fs.existsSync(cliDocsDir)) {
|
|
249
|
+
fs.mkdirSync(cliDocsDir, { recursive: true });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
let syncedCount = 0;
|
|
253
|
+
for (const file of knowledgeFiles) {
|
|
254
|
+
const sourcePath = path.join(stateDir, file);
|
|
255
|
+
const targetPath = path.join(cliDocsDir, file);
|
|
256
|
+
|
|
257
|
+
if (fs.existsSync(sourcePath)) {
|
|
258
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
259
|
+
this.log(`Synced knowledge file: ${file}`);
|
|
260
|
+
syncedCount++;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Update knowledge-sync.json after bridge sync
|
|
265
|
+
if (syncedCount > 0) {
|
|
266
|
+
try {
|
|
267
|
+
const knowledgeSync = require('../../scripts/flow-knowledge-sync');
|
|
268
|
+
knowledgeSync.markAsSynced();
|
|
269
|
+
this.log('Updated knowledge-sync.json');
|
|
270
|
+
} catch (err) {
|
|
271
|
+
// flow-knowledge-sync not available
|
|
272
|
+
this.log(`Could not update knowledge-sync state: ${err.message}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return syncedCount;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Generate and write the main rules file (e.g., CLAUDE.md)
|
|
281
|
+
*/
|
|
282
|
+
generateRulesFile() {
|
|
283
|
+
const config = this.readConfig();
|
|
284
|
+
const content = this.generateRulesContent(config);
|
|
285
|
+
const rulesFilePath = path.join(this.projectDir, this.getRulesFileName());
|
|
286
|
+
|
|
287
|
+
fs.writeFileSync(rulesFilePath, content, 'utf-8');
|
|
288
|
+
this.log(`Generated ${this.getRulesFileName()}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Run full sync: skills, rules, and CLI-specific setup
|
|
293
|
+
* @returns {Object} Sync result summary
|
|
294
|
+
*/
|
|
295
|
+
async sync() {
|
|
296
|
+
const startTime = Date.now();
|
|
297
|
+
const results = {
|
|
298
|
+
cliType: this.cliType,
|
|
299
|
+
cliFolder: this.getCliFolder(),
|
|
300
|
+
synced: [],
|
|
301
|
+
errors: []
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
// 1. Ensure CLI folder exists
|
|
306
|
+
this.ensureCliFolder();
|
|
307
|
+
results.synced.push('cli-folder');
|
|
308
|
+
|
|
309
|
+
// 2. Sync skills
|
|
310
|
+
try {
|
|
311
|
+
this.syncSkills();
|
|
312
|
+
results.synced.push('skills');
|
|
313
|
+
} catch (err) {
|
|
314
|
+
results.errors.push({ step: 'skills', error: err.message });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 3. Sync rules
|
|
318
|
+
try {
|
|
319
|
+
this.syncRules();
|
|
320
|
+
results.synced.push('rules');
|
|
321
|
+
} catch (err) {
|
|
322
|
+
results.errors.push({ step: 'rules', error: err.message });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 4. Sync knowledge files (stack.md, architecture.md, testing.md)
|
|
326
|
+
try {
|
|
327
|
+
const syncedCount = this.syncKnowledgeFiles();
|
|
328
|
+
if (syncedCount > 0) {
|
|
329
|
+
results.synced.push('knowledge-files');
|
|
330
|
+
}
|
|
331
|
+
} catch (err) {
|
|
332
|
+
results.errors.push({ step: 'knowledge-files', error: err.message });
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// 5. Generate rules file
|
|
336
|
+
try {
|
|
337
|
+
this.generateRulesFile();
|
|
338
|
+
results.synced.push('rules-file');
|
|
339
|
+
} catch (err) {
|
|
340
|
+
results.errors.push({ step: 'rules-file', error: err.message });
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 6. CLI-specific setup
|
|
344
|
+
try {
|
|
345
|
+
const config = this.readConfig();
|
|
346
|
+
await this.setupCliSpecific(config);
|
|
347
|
+
results.synced.push('cli-specific');
|
|
348
|
+
} catch (err) {
|
|
349
|
+
results.errors.push({ step: 'cli-specific', error: err.message });
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
} catch (err) {
|
|
353
|
+
results.errors.push({ step: 'general', error: err.message });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
results.duration = Date.now() - startTime;
|
|
357
|
+
results.success = results.errors.length === 0;
|
|
358
|
+
|
|
359
|
+
return results;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ==================== Utility Methods ====================
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Copy a directory recursively
|
|
366
|
+
*/
|
|
367
|
+
copyDirRecursive(source, target) {
|
|
368
|
+
if (!fs.existsSync(target)) {
|
|
369
|
+
fs.mkdirSync(target, { recursive: true });
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const items = fs.readdirSync(source);
|
|
373
|
+
for (const item of items) {
|
|
374
|
+
const sourcePath = path.join(source, item);
|
|
375
|
+
const targetPath = path.join(target, item);
|
|
376
|
+
|
|
377
|
+
if (fs.statSync(sourcePath).isDirectory()) {
|
|
378
|
+
this.copyDirRecursive(sourcePath, targetPath);
|
|
379
|
+
} else {
|
|
380
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Log a message (if verbose mode is on)
|
|
387
|
+
*/
|
|
388
|
+
log(message) {
|
|
389
|
+
if (this.verbose) {
|
|
390
|
+
console.log(`[${this.cliType}] ${message}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
module.exports = BaseBridge;
|