@abarcenas/ai-workflow-template 1.23.0 → 1.25.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/.agents/instructions/learned-knowledge.instructions.md +17 -41
- package/.agents/instructions/memory-bank.instructions.md +56 -2
- package/.agents/instructions/task-implementation.instructions.md +12 -5
- package/.agents/skills/architecture-blueprint-generator/SKILL.md +63 -18
- package/.agents/skills/context-map/SKILL.md +27 -4
- package/.agents/skills/graphify-framework-aware/SKILL.md +133 -0
- package/.agents/skills/graphify-framework-aware/references/framework-registry.md +95 -0
- package/.agents/skills/graphify-framework-aware/scripts/fw-exclude.py +425 -0
- package/AGENTS.md +54 -0
- package/README.md +105 -2
- package/opencode.mcp.example.json +1 -0
- package/package.json +7 -2
- package/scripts/sync.test.js +12 -0
|
@@ -1,46 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
applyTo: "**"
|
|
3
|
-
description: "Persistent project knowledge accumulated across sessions. This file grows over time as agents discover patterns, conventions, gotchas, and preferences during work. It enables self-improvement — each session learns from the last."
|
|
4
|
-
---
|
|
1
|
+
# Learned Knowledge
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
> Accumulated patterns, conventions, gotchas, and agent tuning notes from TDD and other pipeline sessions.
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
## Session: 2026-06-13
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
**Pipeline:** TDD Infrastructure Bootstrap
|
|
8
|
+
**Coverage:** N/A (infrastructure setup)
|
|
9
|
+
**TDD Iterations:** 0
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
11
|
+
**New knowledge:**
|
|
12
|
+
- Project is a template distribution package (npm), not a deployable application
|
|
13
|
+
- Source code is in `scripts/` (ES modules), not a traditional `src/` directory
|
|
14
|
+
- E2E tests use Playwright with `tests/` directory, `.spec.ts` extension
|
|
15
|
+
- Unit tests use Vitest with `scripts/` directory, `.test.{js,ts}` extension
|
|
16
|
+
- Playwright config uses `chromium` as default, `fullyParallel: true`
|
|
17
|
+
- Coverage threshold is 90% across all metrics (statements, branches, functions, lines)
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<!-- High-level project description filled in over time -->
|
|
21
|
-
|
|
22
|
-
## Conventions & Patterns
|
|
23
|
-
|
|
24
|
-
<!-- Coding conventions, architectural patterns, naming conventions discovered in this project -->
|
|
25
|
-
|
|
26
|
-
## Gotchas & Deviations
|
|
27
|
-
|
|
28
|
-
<!-- Unexpected behaviors, tricky configs, known pitfalls -->
|
|
29
|
-
|
|
30
|
-
## Preferences
|
|
31
|
-
|
|
32
|
-
<!-- User preferences about tooling, style, approach, etc. -->
|
|
33
|
-
|
|
34
|
-
## Architecture Decisions
|
|
35
|
-
|
|
36
|
-
<!-- Key technical decisions and their rationale -->
|
|
37
|
-
|
|
38
|
-
## Agent Tuning Notes
|
|
39
|
-
|
|
40
|
-
<!-- Per-agent prompt improvements discovered during sessions -->
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## Session Log
|
|
45
|
-
|
|
46
|
-
<!-- New entries are appended here by the orchestrator after each pipeline -->
|
|
19
|
+
**Agent tuning notes:**
|
|
20
|
+
- unit-tester: Must be told to look in `scripts/` for source and test files, not `src/`
|
|
21
|
+
- coder: Must be told the project uses ES module syntax (import/export, not require/module.exports)
|
|
22
|
+
- implementer: Plans should target `scripts/` for production code and tests
|
|
@@ -70,6 +70,20 @@ flowchart TD
|
|
|
70
70
|
- Includes task index file (`_index.md`) listing all tasks with their statuses
|
|
71
71
|
- Preserves complete thought process and history for each task
|
|
72
72
|
|
|
73
|
+
### Graph-Enhanced Context (graphify)
|
|
74
|
+
|
|
75
|
+
When `graphify-out/graph.json` exists, it serves as a complementary machine-readable index of all file relationships, dependencies, and architectural patterns. The memory bank's markdown files capture human-written project context; graphify captures automatically extracted code-level connections.
|
|
76
|
+
|
|
77
|
+
**Bootstrap:** If `graphify-out/graph.json` does not exist, run `graphify .` to generate the knowledge graph before reading any memory bank files. This ensures code-level relationship data is available alongside the markdown context.
|
|
78
|
+
|
|
79
|
+
**When to query the graph instead of reading files:**
|
|
80
|
+
- Architecture and dependency questions → `graphify query` (subgraph results, ~200-800 tokens)
|
|
81
|
+
- Component interaction tracing → `graphify path "<A>" "<B>"`
|
|
82
|
+
- Focused concept deep-dives → `graphify explain "<concept>"`
|
|
83
|
+
- Community/pattern overview → `graphify-out/GRAPH_REPORT.md`
|
|
84
|
+
|
|
85
|
+
**Keeping the graph current:** After any code modification, run `graphify update .` (AST-only, zero API cost) to sync the graph.
|
|
86
|
+
|
|
73
87
|
### Additional Context
|
|
74
88
|
|
|
75
89
|
Create additional files/folders within memory-bank/ when they help organize:
|
|
@@ -86,8 +100,12 @@ Create additional files/folders within memory-bank/ when they help organize:
|
|
|
86
100
|
|
|
87
101
|
```mermaid
|
|
88
102
|
flowchart TD
|
|
89
|
-
Start[Start] -->
|
|
90
|
-
|
|
103
|
+
Start[Start] --> CheckGraph{graphify-out/graph.json exists?}
|
|
104
|
+
CheckGraph -->|No| Bootstrap[Run graphify .]
|
|
105
|
+
Bootstrap --> ReadFiles[Read Memory Bank]
|
|
106
|
+
CheckGraph -->|Yes| ReadFiles
|
|
107
|
+
ReadFiles --> QueryGraph[Query graphify for architecture context]
|
|
108
|
+
QueryGraph --> CheckFiles{Files Complete?}
|
|
91
109
|
|
|
92
110
|
CheckFiles -->|No| Plan[Create Plan]
|
|
93
111
|
Plan --> Document[Document in Chat]
|
|
@@ -139,6 +157,41 @@ For every task:
|
|
|
139
157
|
|
|
140
158
|
The Markdown record is the source of truth for auditing, traceability, and recovery after session resets.
|
|
141
159
|
|
|
160
|
+
## Graphify-Enhanced Context Retrieval
|
|
161
|
+
|
|
162
|
+
When `graphify-out/graph.json` exists, prefer graphify over grep/glob for codebase questions:
|
|
163
|
+
|
|
164
|
+
| Question Type | Graphify Command | Replaces |
|
|
165
|
+
|---|---|---|
|
|
166
|
+
| How does X work? | `graphify explain "X"` | Grep for definitions, reading multiple files |
|
|
167
|
+
| What calls Y? | `graphify query "calls Y depends-on Y invokes Y"` | Recursive grep for callers across files |
|
|
168
|
+
| How does A connect to B? | `graphify path "A" "B"` | Manual dependency tracing across files |
|
|
169
|
+
| Architecture overview | `graphify query "architecture layers patterns dependencies"` | GRAPH_REPORT.md / file-by-file analysis |
|
|
170
|
+
| Community structure | `graphify-out/GRAPH_REPORT.md` `## Communities` | Manual folder hierarchy analysis |
|
|
171
|
+
| Key abstractions | `graphify query "interface abstract class base pattern"` | Grep for interface/abstract class definitions |
|
|
172
|
+
|
|
173
|
+
**Token efficiency:** graphify queries return focused subgraphs (~200-800 tokens) vs. raw grep results (often 3000+ tokens for large codebases). Always query the graph first; fall back to grep only when graphify lacks sufficient data or `graphify-out/graph.json` is missing.
|
|
174
|
+
|
|
175
|
+
**Expanding queries:** When a graphify query returns 0 hits, expand the question against the graph's vocabulary before falling back to grep. Run:
|
|
176
|
+
```
|
|
177
|
+
$(cat graphify-out/.graphify_python) -c "
|
|
178
|
+
import json, re
|
|
179
|
+
from pathlib import Path
|
|
180
|
+
data = json.loads(Path('graphify-out/graph.json').read_text())
|
|
181
|
+
vocab = set()
|
|
182
|
+
for n in data['nodes']:
|
|
183
|
+
for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE):
|
|
184
|
+
parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c]
|
|
185
|
+
for p in parts:
|
|
186
|
+
t = p.lower()
|
|
187
|
+
if 3 <= len(t) <= 30:
|
|
188
|
+
vocab.add(t)
|
|
189
|
+
Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab)))
|
|
190
|
+
print(f'vocab: {len(vocab)} tokens')
|
|
191
|
+
"
|
|
192
|
+
```
|
|
193
|
+
Then select up to 12 tokens from the vocabulary list that semantically match the query intent, and re-run with the expanded query.
|
|
194
|
+
|
|
142
195
|
## Documentation Updates
|
|
143
196
|
|
|
144
197
|
Memory Bank updates occur when:
|
|
@@ -147,6 +200,7 @@ Memory Bank updates occur when:
|
|
|
147
200
|
2. After implementing significant changes
|
|
148
201
|
3. When user requests with **update memory bank** (MUST review ALL files)
|
|
149
202
|
4. When context needs clarification
|
|
203
|
+
5. After code changes that introduce new relationships — run `graphify update .` to sync the graph
|
|
150
204
|
|
|
151
205
|
```mermaid
|
|
152
206
|
flowchart TD
|
|
@@ -22,12 +22,16 @@ All planning, implementation, and execution work must be recorded in Markdown fi
|
|
|
22
22
|
|
|
23
23
|
**MUST complete before starting implementation:**
|
|
24
24
|
|
|
25
|
+
0. **Verify graphify graph:** Check `graphify-out/graph.json` exists. If not, run `graphify .` to generate it. The graph provides code-level relationship data (dependencies, callers, patterns) that the memory bank's markdown docs alone cannot capture. Skip this step only if the task exclusively modifies markdown/docs files.
|
|
25
26
|
1. Read `memory-bank/activeContext.md` to understand current focus and recent changes
|
|
26
27
|
2. Read `memory-bank/progress.md` to understand project-wide status
|
|
27
28
|
3. Read `memory-bank/tasks/_index.md` to find the task assigned to you
|
|
28
29
|
4. Read the full task file in `memory-bank/tasks/` — understand the request, thought process, implementation plan, and all subtasks
|
|
29
|
-
5.
|
|
30
|
-
|
|
30
|
+
5. **Graphify context for referenced files:** For each referenced file mentioned in the task:
|
|
31
|
+
- Run `graphify explain "<file-or-component>"` to get all connected nodes and their relationships
|
|
32
|
+
- Run `graphify path "<task-relevant-component>" "<referenced-file>"` to trace dependency chains
|
|
33
|
+
- Fall back to the Read tool only when graphify lacks detail or the file is not in the graph
|
|
34
|
+
6. Understand current project structure and conventions via the graph's community structure (`graphify-out/GRAPH_REPORT.md` `## Communities`) and the memory bank's `systemPatterns.md`
|
|
31
35
|
|
|
32
36
|
## 2. Systematic Implementation Process
|
|
33
37
|
|
|
@@ -36,7 +40,7 @@ All planning, implementation, and execution work must be recorded in Markdown fi
|
|
|
36
40
|
1. **Process subtasks in order** — Follow the task file's subtask sequence exactly, one step at a time
|
|
37
41
|
2. **Before implementing any subtask:**
|
|
38
42
|
- Fully understand all implementation requirements from the task file
|
|
39
|
-
- Gather any additional required context
|
|
43
|
+
- Gather any additional required context — prefer `graphify query "<subtask topic>"` over grep/glob for discovering related files, callers, and patterns
|
|
40
44
|
3. **Implement the subtask completely with working code:**
|
|
41
45
|
- Follow existing code patterns and conventions
|
|
42
46
|
- Create working functionality that meets all requirements
|
|
@@ -74,6 +78,7 @@ All planning, implementation, and execution work must be recorded in Markdown fi
|
|
|
74
78
|
2. Update `memory-bank/activeContext.md` — summarize what was implemented and set next steps
|
|
75
79
|
3. Update `memory-bank/tasks/_index.md` — mark the task file as Completed
|
|
76
80
|
4. Mark the task's overall status to Completed in its own file
|
|
81
|
+
5. Run `graphify update .` to sync the knowledge graph with newly implemented code (AST-only, zero API cost)
|
|
77
82
|
|
|
78
83
|
## 5. Problem Resolution
|
|
79
84
|
|
|
@@ -88,16 +93,18 @@ All planning, implementation, and execution work must be recorded in Markdown fi
|
|
|
88
93
|
## Implementation Workflow
|
|
89
94
|
|
|
90
95
|
```
|
|
96
|
+
0. Verify graphify-out/graph.json exists (run `graphify .` if not)
|
|
91
97
|
1. Read activeContext.md, progress.md, tasks/_index.md, and the assigned task file
|
|
92
98
|
2. For each unchecked subtask:
|
|
93
|
-
a. Fully understand requirements
|
|
99
|
+
a. Fully understand requirements — query graphify for context
|
|
94
100
|
b. Implement with working code following workspace patterns
|
|
95
101
|
c. Validate implementation meets requirements
|
|
96
102
|
d. Mark subtask complete in task file
|
|
97
103
|
e. Add progress log entry to task file with date and details
|
|
98
104
|
f. Update activeContext.md and _index.md
|
|
99
105
|
3. Repeat until all subtasks complete
|
|
100
|
-
4.
|
|
106
|
+
4. Run `graphify update .` to sync graph
|
|
107
|
+
5. Update progress.md, activeContext.md, _index.md with final status
|
|
101
108
|
```
|
|
102
109
|
|
|
103
110
|
## Success Criteria
|
|
@@ -5,6 +5,9 @@ description: 'Comprehensive project architecture blueprint generator that analyz
|
|
|
5
5
|
|
|
6
6
|
# Comprehensive Project Architecture Blueprint Generator
|
|
7
7
|
|
|
8
|
+
> **Pre-discovery:** Load the `context-map` skill first to build a scoped file-level map via graphify. The context map feeds into the full blueprint below.
|
|
9
|
+
> **Graphify:** All codebase discovery uses `graphify query`, `graphify path`, and `graphify explain` against `graphify-out/graph.json`. Grep/glob/read are fallbacks only when graphify returns no results.
|
|
10
|
+
|
|
8
11
|
## Configuration Variables
|
|
9
12
|
${PROJECT_TYPE="Auto-detect|.NET|Java|React|Angular|Python|Node.js|Flutter|Other"} <!-- Primary technology -->
|
|
10
13
|
${ARCHITECTURE_PATTERN="Auto-detect|Clean Architecture|Microservices|Layered|MVVM|MVC|Hexagonal|Event-Driven|Serverless|Monolithic|Other"} <!-- Primary architectural pattern -->
|
|
@@ -19,18 +22,45 @@ ${FOCUS_ON_EXTENSIBILITY=true|false} <!-- Emphasize extension points and pattern
|
|
|
19
22
|
|
|
20
23
|
"Create a comprehensive 'Project_Architecture_Blueprint.md' document that thoroughly analyzes the architectural patterns in the codebase to serve as a definitive reference for maintaining architectural consistency. Use the following approach:
|
|
21
24
|
|
|
25
|
+
### 0. Graphify-First Discovery
|
|
26
|
+
|
|
27
|
+
Before any grep, glob, or manual file reads, leverage the project's knowledge graph for token-efficient discovery:
|
|
28
|
+
|
|
29
|
+
**0a. Bootstrap the graph if needed:**
|
|
30
|
+
If `graphify-out/graph.json` does not exist, generate it first:
|
|
31
|
+
```
|
|
32
|
+
graphify .
|
|
33
|
+
```
|
|
34
|
+
This builds the knowledge graph with AST-extracted relationships and community structure. All subsequent discovery steps query the graph.
|
|
35
|
+
|
|
36
|
+
**0b. Architecture detection via graph queries (primary):**
|
|
37
|
+
- `graphify query "architecture pattern layers dependencies"` — discover architectural structure
|
|
38
|
+
- `graphify query "technology stack frameworks packages imports"` — detect tech stack
|
|
39
|
+
- `graphify query "key interfaces abstractions design patterns"` — identify core abstractions
|
|
40
|
+
- `graphify query "cross-cutting concerns authentication logging configuration"` — find cross-cutting concern implementations
|
|
41
|
+
|
|
42
|
+
**0c. Relationship mapping via graph (primary):**
|
|
43
|
+
- `graphify path "<ComponentA>" "<ComponentB>"` — trace dependency chains between subsystems
|
|
44
|
+
- `graphify explain "<component>"` — deep-dive on a single node's connections and patterns
|
|
45
|
+
- `graphify query "circular dependency"` — detect layer violations and dependency cycles
|
|
46
|
+
|
|
47
|
+
**0d. Broad architecture scan:**
|
|
48
|
+
Read `graphify-out/GRAPH_REPORT.md` `## Communities` and `## God Nodes` sections for community structure and hub nodes. Use `graphify-out/wiki/index.md` for agent-navigable wiki if it exists.
|
|
49
|
+
|
|
50
|
+
**0e. Fallback to grep/glob/read:**
|
|
51
|
+
Only when graphify queries return no relevant results (rare for well-structured codebases), fall back to grep/glob/file-reads. Graphify subgraph results are typically 200-800 tokens vs. 3000+ tokens for raw grep output.
|
|
52
|
+
|
|
22
53
|
### 1. Architecture Detection and Analysis
|
|
23
|
-
- ${PROJECT_TYPE == "Auto-detect" ? "
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
- Communication mechanisms between components" : "Document how the ${ARCHITECTURE_PATTERN} architecture is implemented"}
|
|
54
|
+
- ${PROJECT_TYPE == "Auto-detect" ? "Use graphify queries to identify all technology stacks and frameworks in use:
|
|
55
|
+
- `graphify query \"technology stack frameworks dependencies imports\"` for tech stack
|
|
56
|
+
- `graphify query \"build configuration deployment infrastructure\"` for build/deploy config
|
|
57
|
+
- Read `graphify-out/GRAPH_REPORT.md` `## Communities` for community-level technology groupings" : "Focus on ${PROJECT_TYPE} specific patterns and practices"}
|
|
58
|
+
|
|
59
|
+
- ${ARCHITECTURE_PATTERN == "Auto-detect" ? "Determine the architectural pattern(s) by querying the graph structure:
|
|
60
|
+
- `graphify query \"layer dependency direction boundaries\"` reveals dependency flow
|
|
61
|
+
- `graphify query \"interfaces abstractions contract segregation\"` reveals interface patterns
|
|
62
|
+
- `graphify path` between key subsystems reveals communication mechanisms
|
|
63
|
+
- `graphify explain \"<layer-name>\"` deep-dives into each architectural layer" : "Document how the ${ARCHITECTURE_PATTERN} architecture is implemented"}
|
|
34
64
|
|
|
35
65
|
### 2. Architectural Overview
|
|
36
66
|
- Provide a clear, concise explanation of the overall architectural approach
|
|
@@ -39,17 +69,29 @@ ${FOCUS_ON_EXTENSIBILITY=true|false} <!-- Emphasize extension points and pattern
|
|
|
39
69
|
- Note any hybrid architectural patterns or adaptations of standard patterns
|
|
40
70
|
|
|
41
71
|
### 3. Architecture Visualization
|
|
72
|
+
|
|
73
|
+
Extract component relationships from the graph before building diagrams:
|
|
74
|
+
- `graphify query "component subsystem module relationships dependencies"` — discover subsystems
|
|
75
|
+
- `graphify path "<SubsystemA>" "<SubsystemB>"` — verify dependency directions between specific subsystems
|
|
76
|
+
- `graphify explain "<subsystem>"` — deep-dive into a subsystem's internal structure and connections
|
|
77
|
+
- Use `graphify-out/GRAPH_REPORT.md` `## Surprising Connections` for non-obvious cross-boundary links
|
|
78
|
+
|
|
42
79
|
${DIAGRAM_TYPE != "None" ? `Create ${DIAGRAM_TYPE} diagrams at multiple levels of abstraction:
|
|
43
|
-
- High-level architectural overview showing major subsystems
|
|
44
|
-
- Component interaction diagrams showing relationships and dependencies
|
|
45
|
-
- Data flow diagrams showing how information moves through the system
|
|
80
|
+
- High-level architectural overview showing major subsystems (from graph community structure)
|
|
81
|
+
- Component interaction diagrams showing relationships and dependencies (from graph edges)
|
|
82
|
+
- Data flow diagrams showing how information moves through the system (from `graphify path` traces)
|
|
46
83
|
- Ensure diagrams accurately reflect the actual implementation, not theoretical patterns` : "Describe the component relationships based on actual code dependencies, providing clear textual explanations of:
|
|
47
|
-
- Subsystem organization and boundaries
|
|
48
|
-
- Dependency directions and component interactions
|
|
49
|
-
- Data flow and process sequences"}
|
|
84
|
+
- Subsystem organization and boundaries (from graph communities)
|
|
85
|
+
- Dependency directions and component interactions (from graph edges)
|
|
86
|
+
- Data flow and process sequences (from `graphify path` traces)"}
|
|
50
87
|
|
|
51
88
|
### 4. Core Architectural Components
|
|
52
|
-
|
|
89
|
+
Discover components via the graph first, then deep-dive:
|
|
90
|
+
- Identify major components from `graphify-out/GRAPH_REPORT.md` `## God Nodes` (hub nodes with highest degree)
|
|
91
|
+
- Use `graphify explain "<component>"` to get all connected nodes and relationships for each god node
|
|
92
|
+
- Use `graphify path "<component>" "<other>"` to trace specific interaction patterns between components
|
|
93
|
+
|
|
94
|
+
For each architectural component discovered:
|
|
53
95
|
|
|
54
96
|
- **Purpose and Responsibility**:
|
|
55
97
|
- Primary function within the architecture
|
|
@@ -321,6 +363,9 @@ Create a clear architectural guide for implementing new features:
|
|
|
321
363
|
|
|
322
364
|
Include information about when this blueprint was generated and recommendations for keeping it updated as the architecture evolves.
|
|
323
365
|
|
|
366
|
+
### Post-Generation: Keep Graph Current
|
|
367
|
+
After the blueprint is generated, run `graphify update .` to incrementally re-extract any changed files. This keeps the graph aligned with newly documented architecture understanding, so future queries (`graphify query`, `graphify path`, `graphify explain`) return up-to-date results without rebuilding the entire graph.
|
|
368
|
+
|
|
324
369
|
### ${INCLUDES_DECISION_RECORDS ? "18" : INCLUDES_CODE_EXAMPLES ? "17" : "16"}. Populate Agent Knowledge Base
|
|
325
370
|
|
|
326
371
|
Write the discovered architecture details into the persistent knowledge system so all orchestrator agents (researcher, architect, implementer, coder, tracker) inherit this context without rediscovery:
|
|
@@ -5,6 +5,9 @@ description: "Generate a map of all files relevant to a task before making chang
|
|
|
5
5
|
|
|
6
6
|
# Context Map
|
|
7
7
|
|
|
8
|
+
> **Wired into:** `architecture-blueprint-generator` loads this skill as its pre-discovery step to build a scoped file-level map before generating the full blueprint.
|
|
9
|
+
> **Graphify:** All discovery uses `graphify query`, `graphify path`, and `graphify explain` against `graphify-out/graph.json`. Grep/glob are fallbacks only.
|
|
10
|
+
|
|
8
11
|
Before implementing any changes, analyze the codebase and create a context map.
|
|
9
12
|
|
|
10
13
|
## Task
|
|
@@ -13,10 +16,30 @@ Before implementing any changes, analyze the codebase and create a context map.
|
|
|
13
16
|
|
|
14
17
|
## Instructions
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
### Graphify-First Discovery
|
|
20
|
+
|
|
21
|
+
1. **Bootstrap graph if needed:** Check `graphify-out/graph.json`. If missing, run `graphify .` first. The graph provides relationship data that grep alone cannot discover.
|
|
22
|
+
|
|
23
|
+
2. **Discover related files:** Use `graphify query "{{task_description}}"` to find files and nodes relevant to the task. For multi-concept tasks, run separate queries per concept and merge results.
|
|
24
|
+
|
|
25
|
+
3. **Identify direct dependencies:** Use `graphify path "<source>" "<target>"` between key components to trace dependency chains. This reveals import/export relationships without scanning every file.
|
|
26
|
+
|
|
27
|
+
4. **Find related tests:** Use `graphify query "test spec <task_keywords>"` to surface test files connected to the relevant components.
|
|
28
|
+
|
|
29
|
+
5. **Discover similar patterns:** Use `graphify explain "<pattern_or_component>"` to find existing implementations and connected nodes that follow the same pattern.
|
|
30
|
+
|
|
31
|
+
### Fallback (graphify unavailable or no results)
|
|
32
|
+
|
|
33
|
+
If `graphify-out/graph.json` is missing and cannot be generated, or queries return no results:
|
|
34
|
+
|
|
35
|
+
1. Search the codebase for files related to this task (grep/glob)
|
|
36
|
+
2. Identify direct dependencies (imports/exports via grep)
|
|
37
|
+
3. Find related tests (glob `**/*.test.*` / `**/*.spec.*`)
|
|
38
|
+
4. Look for similar patterns in existing code (grep for class/function patterns)
|
|
39
|
+
|
|
40
|
+
### Post-Generation
|
|
41
|
+
|
|
42
|
+
After the context map is created, run `graphify update .` to sync the graph with any newly identified relationships. This keeps future context-map invocations efficient.
|
|
20
43
|
|
|
21
44
|
## Output Format
|
|
22
45
|
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: graphify-framework-aware
|
|
3
|
+
description: Prepares framework-aware exclusions before graphify knowledge graph generation. Invoke alongside /graphify. Automatically detects Angular, Hugo, Next.js, Svelte, Jekyll, Flutter, WordPress, and Docusaurus projects and excludes their boilerplate files so the graph focuses on your custom code. Triggers on: /graphify, graphify, knowledge graph, codebase graph.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: Bash
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# graphify-framework-aware
|
|
9
|
+
|
|
10
|
+
Automatically detects your project's framework and configures `.graphifyignore` to exclude framework boilerplate so graphify's knowledge graph focuses on your custom code.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
Invoke this skill BEFORE running the standard `/graphify` pipeline. The skill is idempotent — running it multiple times is safe, and it will not duplicate patterns.
|
|
15
|
+
|
|
16
|
+
Use when:
|
|
17
|
+
- Running `/graphify` on a project for the first time
|
|
18
|
+
- Running `/graphify --update` to rebuild an existing graph
|
|
19
|
+
- After adding a new framework to a project
|
|
20
|
+
- After changing build tooling or project structure
|
|
21
|
+
|
|
22
|
+
Skip when:
|
|
23
|
+
- The project has no supported framework markers
|
|
24
|
+
- The `.graphifyignore` was already generated and the project structure hasn't changed
|
|
25
|
+
- Graph is already built and framework exclusions were already applied
|
|
26
|
+
|
|
27
|
+
## Workflow
|
|
28
|
+
|
|
29
|
+
### Step 1: Apply framework exclusions
|
|
30
|
+
|
|
31
|
+
Run the detection and apply script:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
python3 <skill-base>/scripts/fw-exclude.py apply <project-path>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This script:
|
|
38
|
+
1. Scans the project root for framework marker files (e.g., `angular.json`, `hugo.toml`)
|
|
39
|
+
2. Generates appropriate `.graphifyignore` patterns for each detected framework
|
|
40
|
+
3. Idempotently writes/updates `.graphifyignore` between marker comments
|
|
41
|
+
|
|
42
|
+
The script outputs a one-line summary:
|
|
43
|
+
```
|
|
44
|
+
graphify-framework-aware: detected angular, nextjs — wrote 8 exclusion patterns to .graphifyignore
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If no frameworks are detected, the script exits with code 2 and prints:
|
|
48
|
+
```
|
|
49
|
+
No supported frameworks detected.
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Step 2: Inspect the result (optional)
|
|
53
|
+
|
|
54
|
+
To preview what will be excluded without modifying files:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
python3 <skill-base>/scripts/fw-exclude.py show <project-path>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
To see detected frameworks as JSON:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
python3 <skill-base>/scripts/fw-exclude.py detect <project-path>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Step 3: Proceed with graphify
|
|
67
|
+
|
|
68
|
+
After exclusions are applied, invoke the standard graphify pipeline:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
invoke skill: "graphify"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The `.graphifyignore` file will be read by graphify's detection step, and framework boilerplate files will be excluded from the knowledge graph.
|
|
75
|
+
|
|
76
|
+
### Step 4: Verify (optional)
|
|
77
|
+
|
|
78
|
+
Check the generated `.graphifyignore` at the project root. The framework patterns are in a clearly delimited section:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
# ===== graphify-framework-aware (auto-generated) =====
|
|
82
|
+
# auto-detected framework: angular
|
|
83
|
+
**/*.spec.ts
|
|
84
|
+
**/*.harness.ts
|
|
85
|
+
e2e/
|
|
86
|
+
...
|
|
87
|
+
# ===== end graphify-framework-aware =====
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
User-added patterns outside this block are never modified. To re-include a framework-excluded path, add a `!` negation pattern anywhere *after* the marker block (graphify reads `.graphifyignore` with last-match-wins semantics).
|
|
91
|
+
|
|
92
|
+
## Supported Frameworks
|
|
93
|
+
|
|
94
|
+
| Framework | Marker |
|
|
95
|
+
|-----------|--------|
|
|
96
|
+
| Angular | `angular.json` |
|
|
97
|
+
| Next.js | `next.config.{js,ts,mjs}` |
|
|
98
|
+
| Svelte | `svelte.config.js` |
|
|
99
|
+
| Hugo | `hugo.{toml,yaml,json}` |
|
|
100
|
+
| Jekyll | `_config.yml` |
|
|
101
|
+
| Docusaurus | `docusaurus.config.{js,ts}` |
|
|
102
|
+
| Flutter | `pubspec.yaml` (content-verified) |
|
|
103
|
+
| WordPress | `wp-config.php` |
|
|
104
|
+
|
|
105
|
+
See [Framework Registry](./references/framework-registry.md) for the full list of exclusion patterns per framework and instructions for adding new frameworks.
|
|
106
|
+
|
|
107
|
+
## Script Reference
|
|
108
|
+
|
|
109
|
+
[`scripts/fw-exclude.py`](./scripts/fw-exclude.py) — Python 3.10+, no dependencies beyond stdlib.
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
usage: fw-exclude.py <detect|show|apply> <project-path>
|
|
113
|
+
|
|
114
|
+
detect Print JSON of detected frameworks and their markers.
|
|
115
|
+
show Print FW exclusion patterns (dry-run, no files written).
|
|
116
|
+
apply Idempotently write/update .graphifyignore with FW patterns.
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Interaction with graphify
|
|
120
|
+
|
|
121
|
+
- graphify reads `.graphifyignore` (and falls back to `.gitignore`) during its `detect()` step
|
|
122
|
+
- Framework patterns are appended with last-match-wins semantics — user `!` patterns override them
|
|
123
|
+
- Directories already skipped by graphify's built-in `_SKIP_DIRS` (`node_modules/`, `.next/`, `.angular/`, `dist/`, `build/`, etc.) are NOT duplicated in the generated patterns
|
|
124
|
+
- This skill must run **before** invoking the graphify skill so the `.graphifyignore` is in place when graphify scans files
|
|
125
|
+
|
|
126
|
+
## Troubleshooting
|
|
127
|
+
|
|
128
|
+
| Issue | Solution |
|
|
129
|
+
|-------|----------|
|
|
130
|
+
| "No supported frameworks detected" | Project may use an unsupported framework. Add markers to `fw-exclude.py` or create `.graphifyignore` manually. |
|
|
131
|
+
| Too many files excluded | Check `.graphifyignore` marker block. Add `!` negation patterns after the block to re-include. |
|
|
132
|
+
| `pubspec.yaml` falsely detected as Flutter | Script checks for `flutter` keyword in the file content. Add `!` patterns if needed. |
|
|
133
|
+
| Hugo `config.toml` not detected | Script verifies content for Hugo-specific keys (`baseURL`, `title`). Rename to `hugo.toml` or add patterns manually. |
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Framework Exclusion Registry
|
|
2
|
+
|
|
3
|
+
This document defines the framework detection markers and exclusion patterns used by `graphify-framework-aware`. Each framework entry includes its detection markers, default exclusions, and any special heuristics applied.
|
|
4
|
+
|
|
5
|
+
## Adding a New Framework
|
|
6
|
+
|
|
7
|
+
1. Add detection marker(s) to `FRAMEWORK_MARKERS` in `scripts/fw-exclude.py`.
|
|
8
|
+
2. Add exclusion patterns to `FRAMEWORK_EXCLUDES`.
|
|
9
|
+
3. If complex detection logic is needed (e.g., Hugo theme detection), add it to the `_post_process_excludes()` function.
|
|
10
|
+
4. Update this document with the new framework's entry.
|
|
11
|
+
|
|
12
|
+
## Supported Frameworks
|
|
13
|
+
|
|
14
|
+
### Angular
|
|
15
|
+
|
|
16
|
+
| Attribute | Value |
|
|
17
|
+
|-----------|-------|
|
|
18
|
+
| **Marker file** | `angular.json` |
|
|
19
|
+
| **Excluded** | `**/*.spec.ts`, `**/*.harness.ts`, `e2e/`, `src/environments/`, `karma.conf.js`, `src/test.ts` |
|
|
20
|
+
| **Kept** | `src/app/` (user components, services, modules), `src/assets/` |
|
|
21
|
+
| **Already handled by graphify** | `.angular/`, `node_modules/`, `dist/` |
|
|
22
|
+
|
|
23
|
+
### Next.js
|
|
24
|
+
|
|
25
|
+
| Attribute | Value |
|
|
26
|
+
|-----------|-------|
|
|
27
|
+
| **Marker file** | `next.config.js`, `next.config.ts`, `next.config.mjs` |
|
|
28
|
+
| **Excluded** | `next-env.d.ts`, `out/` |
|
|
29
|
+
| **Kept** | `src/app/`, `src/pages/`, `src/components/` |
|
|
30
|
+
| **Already handled by graphify** | `.next/`, `node_modules/` |
|
|
31
|
+
|
|
32
|
+
### Svelte / SvelteKit
|
|
33
|
+
|
|
34
|
+
| Attribute | Value |
|
|
35
|
+
|-----------|-------|
|
|
36
|
+
| **Marker file** | `svelte.config.js` |
|
|
37
|
+
| **Excluded** | `build/`, `**/*.test.ts` |
|
|
38
|
+
| **Kept** | `src/` (all user code) |
|
|
39
|
+
| **Already handled by graphify** | `.svelte-kit/`, `node_modules/` |
|
|
40
|
+
|
|
41
|
+
### Hugo
|
|
42
|
+
|
|
43
|
+
| Attribute | Value |
|
|
44
|
+
|-----------|-------|
|
|
45
|
+
| **Marker file** | `hugo.toml`, `hugo.yaml`, `hugo.json` |
|
|
46
|
+
| **Excluded (always)** | `public/`, `resources/` |
|
|
47
|
+
| **Excluded (conditional)** | `archetypes/` (unless modified by user), `layouts/` (unless user has custom layouts at root), theme-internal dirs (when theme is vendored via git submodule) |
|
|
48
|
+
| **Kept** | `content/`, `data/`, `static/`, `assets/`, user `layouts/` with custom templates |
|
|
49
|
+
| **Heuristics** | Detects git submodules in `themes/` to exclude theme boilerplate. Detects `layouts/` structure at project root to determine if user has custom templates. |
|
|
50
|
+
|
|
51
|
+
### Jekyll
|
|
52
|
+
|
|
53
|
+
| Attribute | Value |
|
|
54
|
+
|-----------|-------|
|
|
55
|
+
| **Marker file** | `_config.yml` |
|
|
56
|
+
| **Excluded** | `_site/`, `.jekyll-cache/`, `.jekyll-metadata` |
|
|
57
|
+
| **Kept** | `_posts/`, `_pages/`, `_data/`, `_includes/`, `_layouts/`, `_sass/`, `assets/` |
|
|
58
|
+
| **Already handled by graphify** | `node_modules/` |
|
|
59
|
+
|
|
60
|
+
### Docusaurus
|
|
61
|
+
|
|
62
|
+
| Attribute | Value |
|
|
63
|
+
|-----------|-------|
|
|
64
|
+
| **Marker file** | `docusaurus.config.js`, `docusaurus.config.ts` |
|
|
65
|
+
| **Excluded** | `.docusaurus/`, `build/` |
|
|
66
|
+
| **Kept** | `docs/`, `src/`, `blog/`, `static/`, `sidebars.js` |
|
|
67
|
+
| **Already handled by graphify** | `node_modules/` |
|
|
68
|
+
|
|
69
|
+
### Flutter
|
|
70
|
+
|
|
71
|
+
| Attribute | Value |
|
|
72
|
+
|-----------|-------|
|
|
73
|
+
| **Marker file** | `pubspec.yaml` (content-verified for `flutter` keyword) |
|
|
74
|
+
| **Excluded** | `.dart_tool/`, `build/`, `.flutter-plugins`, `.flutter-plugins-dependencies` |
|
|
75
|
+
| **Kept** | `lib/` (user code), `test/` (user tests) |
|
|
76
|
+
| **Already handled by graphify** | — |
|
|
77
|
+
|
|
78
|
+
### WordPress
|
|
79
|
+
|
|
80
|
+
| Attribute | Value |
|
|
81
|
+
|-----------|-------|
|
|
82
|
+
| **Marker file** | `wp-config.php` |
|
|
83
|
+
| **Excluded** | `wp-admin/`, `wp-includes/`, `wp-content/languages/`, `wp-content/upgrade/` |
|
|
84
|
+
| **Kept** | `wp-content/themes/` (custom themes), `wp-content/plugins/` (custom plugins) |
|
|
85
|
+
| **Already handled by graphify** | `node_modules/` |
|
|
86
|
+
|
|
87
|
+
## Noise Directories Handled by graphify (not duplicated)
|
|
88
|
+
|
|
89
|
+
The following directories are already excluded by graphify's built-in `_SKIP_DIRS` list. The framework-aware skill does NOT duplicate these patterns:
|
|
90
|
+
|
|
91
|
+
`.angular/`, `.cache/`, `.dart_tool/`, `.graphify/`, `.idea/`, `.next/`, `.nuxt/`, `.parcel-cache/`, `.pytest_cache/`, `.ruff_cache/`, `.serverless/`, `.svelte-kit/`, `.terraform/`, `.tox/`, `.turbo/`, `.venv/`, `__.pycache__/`, `build/`, `coverage/`, `dist/`, `dist-protected/`, `env/`, `graphify-out/`, `lib64/`, `node_modules/`, `out/`, `site-packages/`, `snapshots/`, `storybook-static/`, `target/`, `venv/`, `visual-tests/`
|
|
92
|
+
|
|
93
|
+
## Noise Files Handled by graphify (not duplicated)
|
|
94
|
+
|
|
95
|
+
`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `Cargo.lock`, `poetry.lock`, `Gemfile.lock`, `composer.lock`, `go.sum`, `go.work.sum`
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""graphify-framework-aware: detect project frameworks and generate .graphifyignore patterns.
|
|
3
|
+
|
|
4
|
+
Commands:
|
|
5
|
+
detect <path> Print JSON of detected frameworks and their markers.
|
|
6
|
+
apply <path> Idempotently write/update .graphifyignore with FW patterns.
|
|
7
|
+
show <path> Print FW patterns only (dry-run, no files written).
|
|
8
|
+
|
|
9
|
+
Exit codes: 0 = success, 1 = error (bad path, invalid args), 2 = nothing to apply.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import re
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
# ── Framework Registry ────────────────────────────────────────────────────────
|
|
22
|
+
# Adding a new framework:
|
|
23
|
+
# 1. Add detection marker(s) to FRAMEWORK_MARKERS.
|
|
24
|
+
# 2. Add exclude patterns to FRAMEWORK_EXCLUDES.
|
|
25
|
+
# Complex detection (Hugo theme) goes in _post_process_excludes().
|
|
26
|
+
|
|
27
|
+
FRAMEWORK_MARKERS: dict[str, list[str]] = {
|
|
28
|
+
"angular": ["angular.json"],
|
|
29
|
+
"nextjs": ["next.config.js", "next.config.ts", "next.config.mjs"],
|
|
30
|
+
"svelte": ["svelte.config.js"],
|
|
31
|
+
"hugo": ["hugo.toml", "hugo.yaml", "hugo.json"],
|
|
32
|
+
"jekyll": ["_config.yml"],
|
|
33
|
+
"docusaurus": ["docusaurus.config.js", "docusaurus.config.ts"],
|
|
34
|
+
"flutter": ["pubspec.yaml"],
|
|
35
|
+
"wordpress": ["wp-config.php"],
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
FRAMEWORK_EXCLUDES: dict[str, list[str]] = {
|
|
39
|
+
"angular": [
|
|
40
|
+
"**/*.spec.ts",
|
|
41
|
+
"**/*.harness.ts",
|
|
42
|
+
"e2e/",
|
|
43
|
+
"src/environments/",
|
|
44
|
+
"karma.conf.js",
|
|
45
|
+
"src/test.ts",
|
|
46
|
+
],
|
|
47
|
+
"nextjs": [
|
|
48
|
+
"next-env.d.ts",
|
|
49
|
+
"out/",
|
|
50
|
+
],
|
|
51
|
+
"svelte": [
|
|
52
|
+
"build/",
|
|
53
|
+
"**/*.test.ts",
|
|
54
|
+
],
|
|
55
|
+
"hugo": [],
|
|
56
|
+
"jekyll": [
|
|
57
|
+
"_site/",
|
|
58
|
+
".jekyll-cache/",
|
|
59
|
+
".jekyll-metadata",
|
|
60
|
+
],
|
|
61
|
+
"docusaurus": [
|
|
62
|
+
".docusaurus/",
|
|
63
|
+
"build/",
|
|
64
|
+
],
|
|
65
|
+
"flutter": [
|
|
66
|
+
".dart_tool/",
|
|
67
|
+
"build/",
|
|
68
|
+
".flutter-plugins",
|
|
69
|
+
".flutter-plugins-dependencies",
|
|
70
|
+
],
|
|
71
|
+
"wordpress": [
|
|
72
|
+
"wp-admin/",
|
|
73
|
+
"wp-includes/",
|
|
74
|
+
"wp-content/languages/",
|
|
75
|
+
"wp-content/upgrade/",
|
|
76
|
+
],
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Marker section boundaries in .graphifyignore
|
|
80
|
+
_MARKER_START = "# ===== graphify-framework-aware (auto-generated) ====="
|
|
81
|
+
_MARKER_END = "# ===== end graphify-framework-aware ====="
|
|
82
|
+
|
|
83
|
+
# Directories graphify already skips natively — don't duplicate them.
|
|
84
|
+
_GRAPHIFY_BUILTIN_SKIP = frozenset({
|
|
85
|
+
"node_modules", "__pycache__", ".git", "dist", "build", "target", "out",
|
|
86
|
+
"site-packages", "lib64", ".pytest_cache", ".mypy_cache", ".ruff_cache",
|
|
87
|
+
".tox", ".eggs", "*.egg-info", "graphify-out",
|
|
88
|
+
"coverage", "lcov-report", "visual-tests", "visual-test",
|
|
89
|
+
"__snapshots__", "snapshots", "storybook-static", "dist-protected",
|
|
90
|
+
".next", ".nuxt", ".turbo", ".angular",
|
|
91
|
+
".idea", ".cache", ".parcel-cache", ".svelte-kit", ".terraform",
|
|
92
|
+
".serverless", ".graphify", ".worktrees",
|
|
93
|
+
"venv", ".venv", "env", ".env",
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
# Lock files graphify already skips.
|
|
97
|
+
_GRAPHIFY_BUILTIN_SKIP_FILES = frozenset({
|
|
98
|
+
"package-lock.json", "yarn.lock", "pnpm-lock.yaml",
|
|
99
|
+
"Cargo.lock", "poetry.lock", "Gemfile.lock",
|
|
100
|
+
"composer.lock", "go.sum", "go.work.sum",
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ── Detection ─────────────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
def _glob_marker(root: Path, pattern: str) -> bool:
|
|
107
|
+
"""Check if a glob pattern matches any file in root (non-recursive by default)."""
|
|
108
|
+
matches = list(root.glob(pattern))
|
|
109
|
+
if matches:
|
|
110
|
+
return any(m.is_file() for m in matches)
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def detect_frameworks(root: Path) -> dict:
|
|
115
|
+
"""Scan project root for framework marker files.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
{"frameworks": ["angular", "nextjs"], "markers": {"angular": ["angular.json"], ...}}
|
|
119
|
+
"""
|
|
120
|
+
root = root.resolve()
|
|
121
|
+
if not root.is_dir():
|
|
122
|
+
print(f"error: not a directory: {root}", file=sys.stderr)
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
|
|
125
|
+
found: dict[str, list[str]] = {}
|
|
126
|
+
for fw_name, markers in FRAMEWORK_MARKERS.items():
|
|
127
|
+
matched = [m for m in markers if _glob_marker(root, m)]
|
|
128
|
+
if matched:
|
|
129
|
+
found[fw_name] = matched
|
|
130
|
+
|
|
131
|
+
# Flutter: pubspec.yaml is generic — verify it's a Flutter project
|
|
132
|
+
if "flutter" in found:
|
|
133
|
+
pubspec = root / "pubspec.yaml"
|
|
134
|
+
try:
|
|
135
|
+
content = pubspec.read_text(encoding="utf-8")
|
|
136
|
+
if "flutter" not in content.lower():
|
|
137
|
+
del found["flutter"]
|
|
138
|
+
except OSError:
|
|
139
|
+
del found["flutter"]
|
|
140
|
+
|
|
141
|
+
# Hugo: config.toml needs content check if not hugo.toml
|
|
142
|
+
# (config.toml is too generic — many non-Hugo projects use it)
|
|
143
|
+
if "hugo" in found:
|
|
144
|
+
hugo_specific = any("hugo" in m.lower() for m in found["hugo"])
|
|
145
|
+
if not hugo_specific:
|
|
146
|
+
# Only matched generic config.toml — verify it's actually Hugo
|
|
147
|
+
config_file = next((root / m for m in found["hugo"] if (root / m).exists()), None)
|
|
148
|
+
if config_file:
|
|
149
|
+
try:
|
|
150
|
+
content = config_file.read_text(encoding="utf-8")
|
|
151
|
+
if "baseURL" not in content and "title" not in content:
|
|
152
|
+
del found["hugo"]
|
|
153
|
+
except OSError:
|
|
154
|
+
del found["hugo"]
|
|
155
|
+
else:
|
|
156
|
+
del found["hugo"]
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
"frameworks": sorted(found.keys()),
|
|
160
|
+
"markers": found,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# ── Exclude pattern generation ────────────────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
def _hugo_theme_is_vendored(root: Path) -> bool:
|
|
167
|
+
"""Check if Hugo themes/ directory contains git submodules or cloned themes."""
|
|
168
|
+
themes_dir = root / "themes"
|
|
169
|
+
if not themes_dir.is_dir():
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
# Check for .gitmodules referencing themes
|
|
173
|
+
gitmodules = root / ".gitmodules"
|
|
174
|
+
if gitmodules.exists():
|
|
175
|
+
try:
|
|
176
|
+
content = gitmodules.read_text(encoding="utf-8")
|
|
177
|
+
if "themes/" in content:
|
|
178
|
+
return True
|
|
179
|
+
except OSError:
|
|
180
|
+
pass
|
|
181
|
+
|
|
182
|
+
# Check if individual themes are git repos (submodules)
|
|
183
|
+
for theme in themes_dir.iterdir():
|
|
184
|
+
if theme.is_dir() and (theme / ".git").exists():
|
|
185
|
+
return True
|
|
186
|
+
|
|
187
|
+
return False
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _hugo_user_has_custom_layouts(root: Path) -> bool:
|
|
191
|
+
"""Check if the user has custom layouts at project root (outside themes/)."""
|
|
192
|
+
layouts = root / "layouts"
|
|
193
|
+
if not layouts.is_dir():
|
|
194
|
+
return False
|
|
195
|
+
# Has custom layouts if more than just _default/ and partials/ exist
|
|
196
|
+
entries = list(layouts.iterdir())
|
|
197
|
+
custom_entries = [e for e in entries
|
|
198
|
+
if e.name not in ("_default", "partials", "shortcodes")]
|
|
199
|
+
return len(custom_entries) > 0
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _post_process_excludes(frameworks: dict, root: Path) -> dict[str, list[str]]:
|
|
203
|
+
"""Apply framework-specific heuristics to refine exclude patterns.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
frameworks: Raw detection result {"frameworks": [...], "markers": {...}}.
|
|
207
|
+
root: Project root path.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
Per-framework final exclude lists (may add/remove patterns).
|
|
211
|
+
"""
|
|
212
|
+
excludes: dict[str, list[str]] = {}
|
|
213
|
+
for fw in frameworks["frameworks"]:
|
|
214
|
+
patterns = list(FRAMEWORK_EXCLUDES.get(fw, []))
|
|
215
|
+
excludes[fw] = patterns
|
|
216
|
+
|
|
217
|
+
# Hugo: refine based on theme/layout detection
|
|
218
|
+
if "hugo" in excludes:
|
|
219
|
+
patterns = excludes["hugo"]
|
|
220
|
+
patterns.append("public/")
|
|
221
|
+
patterns.append("resources/")
|
|
222
|
+
|
|
223
|
+
if _hugo_theme_is_vendored(root):
|
|
224
|
+
patterns.append("archetypes/")
|
|
225
|
+
patterns.append("themes/**/archetypes/")
|
|
226
|
+
patterns.append("themes/**/layouts/")
|
|
227
|
+
else:
|
|
228
|
+
patterns.append("archetypes/")
|
|
229
|
+
|
|
230
|
+
# If user has custom layouts, only exclude defaults
|
|
231
|
+
if not _hugo_user_has_custom_layouts(root):
|
|
232
|
+
patterns.append("layouts/")
|
|
233
|
+
else:
|
|
234
|
+
patterns.append("layouts/_default/")
|
|
235
|
+
patterns.append("layouts/partials/")
|
|
236
|
+
patterns.append("layouts/shortcodes/")
|
|
237
|
+
|
|
238
|
+
return excludes
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# ── .graphifyignore management ────────────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
def _read_graphifyignore(root: Path) -> tuple[str, list[str]]:
|
|
244
|
+
"""Read .graphifyignore and split into preamble / our-section / postscript.
|
|
245
|
+
|
|
246
|
+
Returns: (full_raw_content, lines_matching_our_section)
|
|
247
|
+
"""
|
|
248
|
+
gf = root / ".graphifyignore"
|
|
249
|
+
if not gf.exists():
|
|
250
|
+
return ("", [])
|
|
251
|
+
try:
|
|
252
|
+
content = gf.read_text(encoding="utf-8")
|
|
253
|
+
except OSError:
|
|
254
|
+
return ("", [])
|
|
255
|
+
return (content, _extract_marker_lines(content))
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _extract_marker_lines(content: str) -> list[str]:
|
|
259
|
+
"""Extract lines between our start/end markers (exclusive)."""
|
|
260
|
+
lines = content.splitlines()
|
|
261
|
+
in_block = False
|
|
262
|
+
block_lines: list[str] = []
|
|
263
|
+
for line in lines:
|
|
264
|
+
if line.strip() == _MARKER_START:
|
|
265
|
+
in_block = True
|
|
266
|
+
continue
|
|
267
|
+
if line.strip() == _MARKER_END:
|
|
268
|
+
in_block = False
|
|
269
|
+
continue
|
|
270
|
+
if in_block:
|
|
271
|
+
block_lines.append(line)
|
|
272
|
+
return block_lines
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _normalize_ignore_line(line: str) -> str:
|
|
276
|
+
"""Strip comments and whitespace from a single .graphifyignore line."""
|
|
277
|
+
stripped = line.strip()
|
|
278
|
+
if not stripped or stripped.startswith("#"):
|
|
279
|
+
return ""
|
|
280
|
+
return stripped
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _build_marker_block(excludes: dict[str, list[str]]) -> list[str]:
|
|
284
|
+
"""Build the marker-delimited block of FW patterns."""
|
|
285
|
+
lines: list[str] = [_MARKER_START]
|
|
286
|
+
seen: set[str] = set()
|
|
287
|
+
|
|
288
|
+
for fw_name, patterns in excludes.items():
|
|
289
|
+
lines.append(f"# auto-detected framework: {fw_name}")
|
|
290
|
+
for pat in patterns:
|
|
291
|
+
normalized = _normalize_ignore_line(pat)
|
|
292
|
+
if not normalized or normalized in seen:
|
|
293
|
+
continue
|
|
294
|
+
# Skip patterns graphify already skips natively
|
|
295
|
+
if normalized.rstrip("/") in _GRAPHIFY_BUILTIN_SKIP:
|
|
296
|
+
continue
|
|
297
|
+
if normalized in _GRAPHIFY_BUILTIN_SKIP_FILES:
|
|
298
|
+
continue
|
|
299
|
+
seen.add(normalized)
|
|
300
|
+
lines.append(pat)
|
|
301
|
+
|
|
302
|
+
lines.append(_MARKER_END)
|
|
303
|
+
return lines
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _write_graphifyignore(root: Path, excludes: dict[str, list[str]]) -> str:
|
|
307
|
+
"""Write or update .graphifyignore, replacing our marker section.
|
|
308
|
+
|
|
309
|
+
Returns the summary string describing what was written.
|
|
310
|
+
"""
|
|
311
|
+
gf = root / ".graphifyignore"
|
|
312
|
+
existing_content, existing_block = _read_graphifyignore(root)
|
|
313
|
+
|
|
314
|
+
new_block = _build_marker_block(excludes)
|
|
315
|
+
new_block_text = "\n".join(new_block)
|
|
316
|
+
|
|
317
|
+
# Compare ignoring comments (our own section header comments are stable)
|
|
318
|
+
old_effective = [_normalize_ignore_line(l) for l in existing_block if _normalize_ignore_line(l)]
|
|
319
|
+
new_effective = []
|
|
320
|
+
for l in new_block:
|
|
321
|
+
n = _normalize_ignore_line(l)
|
|
322
|
+
if n and not n.startswith("#"):
|
|
323
|
+
new_effective.append(n)
|
|
324
|
+
|
|
325
|
+
if sorted(old_effective) == sorted(new_effective) and _MARKER_START in existing_content:
|
|
326
|
+
return "unchanged"
|
|
327
|
+
|
|
328
|
+
# Rebuild the file: remove old marker block, insert new one
|
|
329
|
+
if _MARKER_START in existing_content:
|
|
330
|
+
lines = existing_content.splitlines()
|
|
331
|
+
out_lines: list[str] = []
|
|
332
|
+
in_block = False
|
|
333
|
+
for line in lines:
|
|
334
|
+
if line.strip() == _MARKER_START:
|
|
335
|
+
in_block = True
|
|
336
|
+
continue
|
|
337
|
+
if line.strip() == _MARKER_END:
|
|
338
|
+
in_block = False
|
|
339
|
+
continue
|
|
340
|
+
if not in_block:
|
|
341
|
+
out_lines.append(line)
|
|
342
|
+
# Remove trailing blank lines before appending
|
|
343
|
+
while out_lines and out_lines[-1].strip() == "":
|
|
344
|
+
out_lines.pop()
|
|
345
|
+
out_lines.append("")
|
|
346
|
+
out_lines.append(new_block_text)
|
|
347
|
+
result = "\n".join(out_lines)
|
|
348
|
+
if not result.endswith("\n"):
|
|
349
|
+
result += "\n"
|
|
350
|
+
else:
|
|
351
|
+
# No existing block — append
|
|
352
|
+
if existing_content.strip():
|
|
353
|
+
result = existing_content.rstrip() + "\n\n" + new_block_text + "\n"
|
|
354
|
+
else:
|
|
355
|
+
result = new_block_text + "\n"
|
|
356
|
+
|
|
357
|
+
gf.write_text(result, encoding="utf-8")
|
|
358
|
+
|
|
359
|
+
# Build summary
|
|
360
|
+
fw_names = list(excludes.keys())
|
|
361
|
+
pattern_count = len(new_effective)
|
|
362
|
+
if pattern_count == 0:
|
|
363
|
+
return f"frameworks detected ({', '.join(fw_names)}) but no additional patterns needed"
|
|
364
|
+
return (
|
|
365
|
+
f"detected {', '.join(fw_names)} — wrote {pattern_count} "
|
|
366
|
+
f"exclusion pattern{'s' if pattern_count != 1 else ''} to .graphifyignore"
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
# ── CLI ───────────────────────────────────────────────────────────────────────
|
|
371
|
+
|
|
372
|
+
def _cmd_detect(root: Path) -> None:
|
|
373
|
+
result = detect_frameworks(root)
|
|
374
|
+
print(json.dumps(result, indent=2))
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _cmd_show(root: Path) -> None:
|
|
378
|
+
result = detect_frameworks(root)
|
|
379
|
+
if not result["frameworks"]:
|
|
380
|
+
print("No supported frameworks detected.", file=sys.stderr)
|
|
381
|
+
sys.exit(2)
|
|
382
|
+
excludes = _post_process_excludes(result, root)
|
|
383
|
+
for fw, patterns in excludes.items():
|
|
384
|
+
print(f"# {fw}")
|
|
385
|
+
for pat in patterns:
|
|
386
|
+
n = _normalize_ignore_line(pat)
|
|
387
|
+
if n and n not in _GRAPHIFY_BUILTIN_SKIP and n not in _GRAPHIFY_BUILTIN_SKIP_FILES:
|
|
388
|
+
print(pat)
|
|
389
|
+
print()
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _cmd_apply(root: Path) -> None:
|
|
393
|
+
result = detect_frameworks(root)
|
|
394
|
+
if not result["frameworks"]:
|
|
395
|
+
print("No supported frameworks detected.", file=sys.stderr)
|
|
396
|
+
sys.exit(2)
|
|
397
|
+
excludes = _post_process_excludes(result, root)
|
|
398
|
+
summary = _write_graphifyignore(root, excludes)
|
|
399
|
+
print(f"graphify-framework-aware: {summary}")
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
_CMD_TABLE: dict[str, callable] = {
|
|
403
|
+
"detect": _cmd_detect,
|
|
404
|
+
"show": _cmd_show,
|
|
405
|
+
"apply": _cmd_apply,
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def main() -> None:
|
|
410
|
+
if len(sys.argv) < 3:
|
|
411
|
+
print(f"usage: {sys.argv[0]} <detect|show|apply> <project-path>", file=sys.stderr)
|
|
412
|
+
sys.exit(1)
|
|
413
|
+
|
|
414
|
+
cmd = sys.argv[1]
|
|
415
|
+
path = Path(sys.argv[2])
|
|
416
|
+
|
|
417
|
+
if cmd not in _CMD_TABLE:
|
|
418
|
+
print(f"error: unknown command '{cmd}' (expected: detect, show, apply)", file=sys.stderr)
|
|
419
|
+
sys.exit(1)
|
|
420
|
+
|
|
421
|
+
_CMD_TABLE[cmd](path)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
if __name__ == "__main__":
|
|
425
|
+
main()
|
package/AGENTS.md
CHANGED
|
@@ -141,9 +141,63 @@ The agent file is at `.opencode/agents/orchestrator/feature-pipeline.agent.md`.
|
|
|
141
141
|
|
|
142
142
|
**When to use:** You want a predictable, four-step feature pipeline without dynamic agent selection. The orchestrator agent is more flexible; this is more opinionated.
|
|
143
143
|
|
|
144
|
+
### TDD Orchestrator — Test-Driven Development Pipeline
|
|
145
|
+
|
|
146
|
+
Use this when you want strict Test-Driven Development: **tests first, code second**. The pipeline enforces the RED → GREEN → REFACTOR/VERIFY cycle:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
implementer (PLAN) → unit-tester∥ (RED) → coder∥ (GREEN) → unit-tester (VERIFY ≥90%) → reviewer → tracker
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
| Step | Phase | Agent | Role |
|
|
153
|
+
|------|-------|-------|------|
|
|
154
|
+
| 1 | PLAN | implementer | Creates plan with parallel test/code batch tables |
|
|
155
|
+
| 2 | RED | unit-tester∥ | Writes failing tests before any implementation exists |
|
|
156
|
+
| 3 | GREEN | coder∥ | Writes minimal code to pass tests — no extra features |
|
|
157
|
+
| 4 | VERIFY | unit-tester | Runs all tests, checks coverage ≥ 90% |
|
|
158
|
+
| 5 | REVIEW | reviewer | Audits tests + code for quality, security, patterns |
|
|
159
|
+
| 6 | DOCUMENT | tracker | Records results to `docs/tracker-log.md` |
|
|
160
|
+
|
|
161
|
+
The agent file is at `.opencode/agents/orchestrator/tdd-orchestrator.agent.md`.
|
|
162
|
+
|
|
163
|
+
**When to use:** Any feature, bug fix, or refactor where tests should be written first. The orchestrator automatically selects this pipeline when testable code changes are requested.
|
|
164
|
+
|
|
165
|
+
**Coverage gate:** If coverage < 90%, the pipeline loops back to RED phase (up to 3 iterations).
|
|
166
|
+
|
|
167
|
+
### Memory Bank
|
|
168
|
+
|
|
169
|
+
The memory bank at `memory-bank/` preserves project context across AI sessions. Core files:
|
|
170
|
+
|
|
171
|
+
| File | Purpose |
|
|
172
|
+
|------|---------|
|
|
173
|
+
| `projectbrief.md` | Core requirements, goals, scope |
|
|
174
|
+
| `productContext.md` | Problem solved, UX goals |
|
|
175
|
+
| `systemPatterns.md` | Architecture, design patterns |
|
|
176
|
+
| `techContext.md` | Technologies, setup, constraints |
|
|
177
|
+
| `activeContext.md` | Current focus, recent changes, next steps |
|
|
178
|
+
| `progress.md` | What works, what's left, known issues |
|
|
179
|
+
| `tasks/` | Per-task tracking with `_index.md` |
|
|
180
|
+
|
|
181
|
+
**Always read `activeContext.md` and `progress.md` at the start of every task.** Update them after completing work. See `.agents/instructions/memory-bank.instructions.md` for full details.
|
|
182
|
+
|
|
144
183
|
## Additional Resources
|
|
145
184
|
|
|
146
185
|
### Playwright MCP Configuration
|
|
147
186
|
|
|
148
187
|
For details on how Playwright MCP integrates with the headed mode debugging configuration, see:
|
|
149
188
|
- **Docs**: `docs/playwright-mcp-configuration.md`
|
|
189
|
+
|
|
190
|
+
## graphify
|
|
191
|
+
|
|
192
|
+
This project has a knowledge graph at graphify-out/ with god nodes, community structure, and cross-file relationships.
|
|
193
|
+
|
|
194
|
+
When the user types `/graphify`, invoke these skills in order before doing anything else:
|
|
195
|
+
1. `skill: "graphify-framework-aware"` — detects project frameworks and configures `.graphifyignore` to exclude boilerplate
|
|
196
|
+
2. `skill: "graphify"` — runs the knowledge graph pipeline
|
|
197
|
+
|
|
198
|
+
Rules:
|
|
199
|
+
- For codebase questions, first run `graphify query "<question>"` when graphify-out/graph.json exists. Use `graphify path "<A>" "<B>"` for relationships and `graphify explain "<concept>"` for focused concepts. These return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output.
|
|
200
|
+
- Dirty graphify-out/ files are expected after hooks or incremental updates; dirty graph files are not a reason to skip graphify. Only skip graphify if the task is about stale or incorrect graph output, or the user explicitly says not to use it.
|
|
201
|
+
- If graphify-out/wiki/index.md exists, use it for broad navigation instead of raw source browsing.
|
|
202
|
+
- Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context.
|
|
203
|
+
- After modifying code, run `graphify update .` to keep the graph current (AST-only, no API cost).
|
package/README.md
CHANGED
|
@@ -12,12 +12,17 @@ This isn't a one-off template to clone—it's a **live npm package** that distri
|
|
|
12
12
|
|
|
13
13
|
- ⚙️ **27+ Specialized AI Skills** - Reusable, task-based workflows (`.agents/skills/`)
|
|
14
14
|
- 🧠 **Smart Agent Frameworks** - Plan Mode, Implementation Plans, Research Spikes (`.agents/agents/`)
|
|
15
|
+
- 🔄 **Multi-Agent Orchestrator** — Dynamic pipeline builder with 10+ specialized sub-agents
|
|
16
|
+
- 🧪 **TDD Orchestrator** — Strict RED→GREEN→REFACTOR/VERIFY test-driven pipeline (90% coverage gate)
|
|
15
17
|
- 📝 **Framework Instructions** - Technology-specific guidance (`.agents/instructions/`)
|
|
16
18
|
- 💬 **Prompt Templates** - Ready-to-use prompt blueprints (`.agents/prompts/`)
|
|
19
|
+
- 🧠 **Memory Bank** — Persistent project context across AI sessions
|
|
17
20
|
- 🏃♂️ **Caveman Mode** - Ultra-compressed communication for token efficiency
|
|
18
|
-
-
|
|
21
|
+
- 🎭 **Playwright Testing** - Automated E2E testing with MCP integration
|
|
22
|
+
- ⚡ **Vitest Unit Testing** — Fast unit tests with v8 coverage (≥90% threshold)
|
|
19
23
|
- 📦 **Version Auto-Bumping** - Husky-managed semantic versioning
|
|
20
24
|
- 🚀 **Auto-Publishing** - npm publish on merge to `main`
|
|
25
|
+
- 🗺️ **graphify Knowledge Graph** — Queryable code-level relationship mapping
|
|
21
26
|
|
|
22
27
|
---
|
|
23
28
|
|
|
@@ -158,6 +163,7 @@ Some agents require MCP servers configured in `opencode.mcp.json` (copy from `op
|
|
|
158
163
|
- 📋 `create-implementation-plan` - Structured task planning
|
|
159
164
|
- ✍️ `create-readme` - Automated README generation
|
|
160
165
|
- 🎯 `context-map` - File relevance mapping
|
|
166
|
+
- 🧠 `graphify` - Codebase knowledge graph generation
|
|
161
167
|
|
|
162
168
|
### Development Tools
|
|
163
169
|
- 🎭 `playwright-explore-website` / `playwright-generate-test` - Browser automation
|
|
@@ -179,6 +185,84 @@ Some agents require MCP servers configured in `opencode.mcp.json` (copy from `op
|
|
|
179
185
|
|
|
180
186
|
---
|
|
181
187
|
|
|
188
|
+
### 🧠 Knowledge Graph (graphify)
|
|
189
|
+
|
|
190
|
+
Turns any folder of code, docs, papers, images, or videos into a **queryable knowledge graph** with community detection, interactive HTML visualization, and GraphRAG-ready JSON.
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# Build a knowledge graph for the current directory
|
|
194
|
+
/graphify
|
|
195
|
+
|
|
196
|
+
# Build for a specific path
|
|
197
|
+
/graphify <path>
|
|
198
|
+
|
|
199
|
+
# Query an existing graph
|
|
200
|
+
/graphify query "How does authentication work?"
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### Prerequisites
|
|
204
|
+
|
|
205
|
+
- **Python 3.10+** — required runtime
|
|
206
|
+
- **`graphifyy` Python package** — `pip install graphifyy` or `uv tool install graphifyy`
|
|
207
|
+
- **Optional: Gemini API key** (`GEMINI_API_KEY` or `GOOGLE_API_KEY`) — enables faster semantic extraction; without it, graphify falls back to Claude subagents
|
|
208
|
+
|
|
209
|
+
> 📖 [GitHub: safishamsi/graphify](https://github.com/safishamsi/graphify) — full docs, examples, and source
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### 🧪 TDD Orchestrator — Test-Driven Development Pipeline
|
|
214
|
+
|
|
215
|
+
The project enforces a strict **Test-Driven Development** methodology through a specialized orchestrator that runs the **RED → GREEN → REFACTOR/VERIFY** cycle:
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
implementer (PLAN) → unit-tester∥ (RED) → coder∥ (GREEN) → unit-tester (VERIFY) → reviewer → tracker
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
| Step | Agent | Phase | Description |
|
|
222
|
+
|------|-------|-------|-------------|
|
|
223
|
+
| 1 | Implementer | PLAN | Creates implementation plan with parallel test/code batch tables |
|
|
224
|
+
| 2 | Unit Tester | RED | Writes FAILING tests first — defines expected behavior before any code exists |
|
|
225
|
+
| 3 | Coder | GREEN | Writes MINIMAL production code to pass all tests — no extra features |
|
|
226
|
+
| 4 | Unit Tester | VERIFY | Runs full suite, enforces ≥90% coverage threshold |
|
|
227
|
+
| 5 | Reviewer | REFACTOR | Audits tests + implementation for quality, security, patterns |
|
|
228
|
+
| 6 | Tracker | DOCUMENT | Records pipeline results to `docs/tracker-log.md` |
|
|
229
|
+
|
|
230
|
+
**Key TDD rules enforced:**
|
|
231
|
+
- Tests are ALWAYS written first (RED phase) — no implementation until tests exist
|
|
232
|
+
- Coverage gate: if <90%, loop back to RED phase (up to 3 iterations)
|
|
233
|
+
- Every test suite tracked in `memory-bank/tasks/`
|
|
234
|
+
- Lessons learned persisted to `.agents/instructions/learned-knowledge.instructions.md`
|
|
235
|
+
|
|
236
|
+
**How to invoke:**
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
@orchestrator Add user authentication with login/logout endpoints
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The TDD orchestrator uses `subagent_type "tdd-orchestrator"` and auto-selects the strict TDD pipeline when testable code changes are requested.
|
|
243
|
+
|
|
244
|
+
### 🧠 Memory Bank — Persistent Project Context
|
|
245
|
+
|
|
246
|
+
The memory bank preserves project knowledge across AI sessions, preventing context loss after resets:
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
projectbrief.md → productContext.md / systemPatterns.md / techContext.md → activeContext.md → progress.md / tasks/
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
| File | Purpose |
|
|
253
|
+
|------|---------|
|
|
254
|
+
| `memory-bank/projectbrief.md` | Core requirements, goals, and scope |
|
|
255
|
+
| `memory-bank/productContext.md` | Problem solved, UX goals |
|
|
256
|
+
| `memory-bank/systemPatterns.md` | Architecture, design patterns, component relationships |
|
|
257
|
+
| `memory-bank/techContext.md` | Technologies, dev setup, constraints |
|
|
258
|
+
| `memory-bank/activeContext.md` | Current focus, recent changes, next steps |
|
|
259
|
+
| `memory-bank/progress.md` | What works, what's left, known issues |
|
|
260
|
+
| `memory-bank/tasks/` | Per-task tracking with `_index.md` |
|
|
261
|
+
|
|
262
|
+
**Task commands:** `add task`, `update task [ID]`, `show tasks [filter]`
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
182
266
|
## 📦 Versioning
|
|
183
267
|
|
|
184
268
|
Husky automatically bumps versions on every commit:
|
|
@@ -219,10 +303,29 @@ npm run sync -- --dry-run
|
|
|
219
303
|
# Test package creation
|
|
220
304
|
npm pack --json
|
|
221
305
|
|
|
222
|
-
# Run Playwright tests
|
|
306
|
+
# Run Playwright E2E tests
|
|
223
307
|
npm test
|
|
308
|
+
|
|
309
|
+
# Run unit tests (Vitest)
|
|
310
|
+
npm run test:unit
|
|
311
|
+
|
|
312
|
+
# Run unit tests with coverage (90% threshold)
|
|
313
|
+
npm run test:unit:coverage
|
|
314
|
+
|
|
315
|
+
# Watch mode for unit tests
|
|
316
|
+
npm run test:unit:watch
|
|
224
317
|
```
|
|
225
318
|
|
|
319
|
+
### Testing
|
|
320
|
+
|
|
321
|
+
| Type | Framework | Command | Coverage |
|
|
322
|
+
|------|-----------|---------|----------|
|
|
323
|
+
| E2E | Playwright | `npm test` | — |
|
|
324
|
+
| Unit | Vitest | `npm run test:unit` | `npm run test:unit:coverage` (≥90%) |
|
|
325
|
+
|
|
326
|
+
- **E2E tests** live in `tests/*.spec.ts` — browser automation via Playwright
|
|
327
|
+
- **Unit tests** live in `scripts/*.test.{js,ts}` — component tests via Vitest with v8 coverage
|
|
328
|
+
|
|
226
329
|
---
|
|
227
330
|
|
|
228
331
|
## 🔄 Updating the Shared Template
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abarcenas/ai-workflow-template",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.0",
|
|
4
4
|
"description": "A comprehensive template repository for AI-assisted development workflows using GitHub Copilot. This template provides a structured approach to leveraging AI assistance through pre-configured skills, prompts, agents, and coding standards.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
"test:ui": "playwright test --ui",
|
|
10
10
|
"test:debug": "playwright test --debug",
|
|
11
11
|
"test:report": "playwright show-report",
|
|
12
|
+
"test:unit": "vitest run",
|
|
13
|
+
"test:unit:coverage": "vitest run --coverage",
|
|
14
|
+
"test:unit:watch": "vitest",
|
|
12
15
|
"prepare": "husky",
|
|
13
16
|
"sync": "node ./scripts/sync.js",
|
|
14
17
|
"postinstall": "node ./scripts/sync.js"
|
|
@@ -37,7 +40,9 @@
|
|
|
37
40
|
],
|
|
38
41
|
"devDependencies": {
|
|
39
42
|
"@playwright/test": "^1.59.1",
|
|
43
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
40
44
|
"dotenv": "^17.4.2",
|
|
41
|
-
"husky": "^9.0.0"
|
|
45
|
+
"husky": "^9.0.0",
|
|
46
|
+
"vitest": "^4.1.8"
|
|
42
47
|
}
|
|
43
48
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
describe('sync utility functions', () => {
|
|
4
|
+
it('should validate basic assertions work', () => {
|
|
5
|
+
expect(1 + 1).toBe(2)
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
it('should handle string operations', () => {
|
|
9
|
+
const result = 'ai-workflow-template'.split('-').join(' ')
|
|
10
|
+
expect(result).toBe('ai workflow template')
|
|
11
|
+
})
|
|
12
|
+
})
|