@relipa/ai-flow-kit 0.2.0 → 0.2.1

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.
Files changed (38) hide show
  1. package/custom/rules/java/spring-boot-rules.md +209 -0
  2. package/custom/rules/javascript/nestjs-examples.md +41 -0
  3. package/custom/rules/javascript/nestjs-rules.md +42 -0
  4. package/custom/rules/javascript/nodejs-express-examples.md +35 -0
  5. package/custom/rules/javascript/nodejs-express-rules.md +49 -0
  6. package/custom/rules/javascript/reactjs-examples.md +380 -0
  7. package/custom/rules/javascript/reactjs-rules.md +173 -0
  8. package/custom/rules/php/php-examples.md +161 -0
  9. package/custom/rules/php/php-rules.md +127 -0
  10. package/custom/rules/python/python-django-examples.md +34 -0
  11. package/custom/rules/python/python-django-rules.md +48 -0
  12. package/custom/rules/python/python-examples.md +32 -0
  13. package/custom/rules/python/python-fastapi-examples.md +30 -0
  14. package/custom/rules/python/python-fastapi-rules.md +35 -0
  15. package/custom/rules/python/python-ml-examples.md +187 -0
  16. package/custom/rules/python/python-ml-rules.md +121 -0
  17. package/custom/rules/python/python-rules.md +58 -0
  18. package/custom/skills/ba-skills/skill-ba-qna-template-v1.md +4 -4
  19. package/custom/skills/ba-skills/skill-ba-qna-v1.md +6 -0
  20. package/custom/skills/create-system-requirement/SKILL.md +52 -16
  21. package/custom/skills/create-system-requirement/system-requirement-template-v1.md +128 -0
  22. package/custom/skills/impact-analysis/SKILL.md +106 -106
  23. package/custom/skills/report-customer/SKILL.md +99 -99
  24. package/custom/templates/nestjs.md +5 -72
  25. package/custom/templates/nodejs-express.md +5 -73
  26. package/custom/templates/php-plain.md +5 -261
  27. package/custom/templates/php.md +5 -261
  28. package/custom/templates/python-django.md +5 -71
  29. package/custom/templates/python-fastapi.md +5 -54
  30. package/custom/templates/python-ml.md +1 -269
  31. package/custom/templates/python.md +5 -79
  32. package/custom/templates/reactjs.md +5 -492
  33. package/custom/templates/shared/gate-workflow.md +1 -0
  34. package/custom/templates/shared/ml-gate-workflow.md +1 -0
  35. package/custom/templates/spring-boot.md +5 -224
  36. package/docs/common/CHANGELOG.md +20 -10
  37. package/package.json +1 -1
  38. package/scripts/init.js +143 -40
@@ -1,224 +1,5 @@
1
- # Spring Boot AI System Prompt
2
-
3
- You are an expert Java Spring Boot developer. Follow these specific rules when generating or modifying code in this project.
4
-
5
- > **Code examples:** See `.rules/java/spring-boot-examples.md` read it when generating code for a specific layer.
6
-
7
- ---
8
-
9
- ## Project Stack
10
-
11
- - **Java:** 17+
12
- - **Spring Boot:** 3.x
13
- - **Build Tool:** Maven (prefer) or Gradle
14
- - **ORM:** Spring Data JPA + Hibernate
15
- - **Database:** MySQL / PostgreSQL
16
- - **Testing:** JUnit 5 + Mockito + AssertJ
17
- - **Utilities:** Lombok, MapStruct
18
- - **API Style:** RESTful JSON
19
-
20
- ---
21
-
22
- ## Architecture
23
-
24
- Follow strict **Layered Architecture**:
25
-
26
- ```
27
- Controller (HTTP layer)
28
-
29
- Service (Business logic)
30
-
31
- Repository (Data access - Spring Data JPA)
32
-
33
- Entity / Domain Model
34
- ```
35
-
36
- ### Package Structure
37
-
38
- ```
39
- com.company.project/
40
- ├── controller/ # \@RestController — HTTP endpoints only
41
- ├── service/
42
- │ ├── impl/ # \@Service — business logic implementation
43
- │ └── [Interface].java
44
- ├── repository/ # \@Repository — extends JpaRepository
45
- ├── entity/ # \@Entity — JPA entities
46
- ├── dto/
47
- │ ├── request/ # Input DTOs (e.g. CreateUserRequest)
48
- │ └── response/ # Output DTOs (e.g. UserResponse)
49
- ├── mapper/ # MapStruct mappers (Entity ↔ DTO)
50
- ├── exception/
51
- │ ├── GlobalExceptionHandler.java # \@RestControllerAdvice
52
- │ └── [CustomException].java
53
- ├── config/ # \@Configuration classes
54
- └── util/ # Pure utility helpers (stateless)
55
- ```
56
-
57
- ---
58
-
59
- ## Controller Rules
60
-
61
- - Annotate with `\@RestController` + `\@RequestMapping`
62
- - **Only** handle HTTP concerns: parse request, call service, return response
63
- - Never put business logic in Controller
64
- - Always use DTOs — never expose Entity directly
65
- - Use `ResponseEntity<T>` for explicit HTTP status control
66
- - Validate input with `\@Valid` + Bean Validation annotations
67
-
68
- ---
69
-
70
- ## Service Rules
71
-
72
- - Always define an **interface**, implement in the `impl/` package
73
- - Annotate implementation with `\@Service`
74
- - All business logic lives here
75
- - Use `\@Transactional` at the method level (not class level)
76
- - Throw specific custom exceptions, not generic `RuntimeException`
77
- - Never return an Entity — always convert to DTO via Mapper
78
-
79
- ---
80
-
81
- ## Repository Rules
82
-
83
- - Extend `JpaRepository<Entity, ID>`
84
- - Use **method name queries** for simple queries
85
- - Use `\@Query` (JPQL) for complex queries — avoid native SQL unless necessary
86
- - Never add business logic here
87
- - Use `\@EntityGraph` to solve N+1 problems
88
-
89
- ---
90
-
91
- ## Entity Rules
92
-
93
- - Use Lombok: `\@Getter`, `\@Setter`, `\@NoArgsConstructor`, `\@AllArgsConstructor`, `\@Builder`
94
- - Avoid `\@Data` on entities (causes issues with `equals/hashCode` + lazy loading)
95
- - Always use `\@Table(name = "snake_case_table_name")`
96
- - Use `\@Column(name = "snake_case_column_name")` explicitly
97
- - For soft delete: add `deleted` boolean + `deletedAt` timestamp
98
- - Extend `BaseEntity` for audit fields (`createdAt`, `updatedAt`)
99
-
100
- ---
101
-
102
- ## DTO Rules
103
-
104
- - Use separate DTOs for **Request** and **Response** — never share
105
- - Use Lombok: `\@Getter`, `\@Builder`, `\@AllArgsConstructor`, `\@NoArgsConstructor`
106
- - Validate in the Request DTO with Bean Validation (`\@NotBlank`, `\@Email`, `\@NotNull`, `\@Size`)
107
- - Never expose internal fields (password hash, audit timestamps) in the Response DTO
108
-
109
- ---
110
-
111
- ## MapStruct Mapper Rules
112
-
113
- - Use `\@Mapper(componentModel = "spring")` — inject as a Spring bean
114
- - Define explicit mappings with `\@Mapping` when field names differ
115
- - Never do manual mapping (`new DTO(); dto.setField(entity.getField())`)
116
-
117
- ---
118
-
119
- ## Exception Handling
120
-
121
- - Create custom exceptions extending `RuntimeException`
122
- - Handle all exceptions in one `\@RestControllerAdvice` class
123
- - Return a consistent error response format
124
- - Never expose stack traces to the client
125
-
126
- ---
127
-
128
- ## Testing Rules
129
-
130
- ### Unit Tests (Service layer)
131
- - Test class: `[ServiceImpl]Test.java`
132
- - Mock all dependencies with `\@ExtendWith(MockitoExtension.class)` + `\@Mock`
133
- - Test happy path + edge cases + exception scenarios
134
- - Use AssertJ: `assertThat(result).isEqualTo(expected)`
135
-
136
- ### Integration Tests (Controller layer)
137
- - Use `\@SpringBootTest` + `\@AutoConfigureMockMvc`
138
- - Test full HTTP flow with `MockMvc`
139
- - Use `\@Sql` or Testcontainers for database state
140
-
141
- ---
142
-
143
- ## Naming Conventions
144
-
145
- | Element | Convention | Example |
146
- |---------|-----------|---------|
147
- | Class | PascalCase | `UserService`, `OrderController` |
148
- | Method | camelCase | `findById`, `createOrder` |
149
- | Variable | camelCase | `userResponse`, `orderId` |
150
- | Constant | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
151
- | Package | lowercase | `com.company.project.service` |
152
- | DB Table | snake_case | `user_orders`, `product_items` |
153
- | DB Column | snake_case | `created_at`, `user_id` |
154
- | REST endpoint | kebab-case | `/api/v1/user-profiles` |
155
- | Request DTO | `[Action][Resource]Request` | `CreateOrderRequest` |
156
- | Response DTO | `[Resource]Response` | `OrderResponse` |
157
-
158
- ---
159
-
160
- ## API Design
161
-
162
- - Version all APIs: `/api/v1/...`
163
- - Use plural nouns for resources: `/users`, `/orders`
164
- - HTTP methods: `GET` (read), `POST` (create), `PUT` (full update), `PATCH` (partial), `DELETE`
165
- - Return `201 Created` for POST, `200 OK` for GET/PUT/PATCH, `204 No Content` for DELETE
166
- - Use consistent pagination: `?page=0&size=20&sort=createdAt,desc`
167
-
168
- ```
169
- GET /api/v1/users → 200 list
170
- POST /api/v1/users → 201 created
171
- GET /api/v1/users/{id} → 200 or 404
172
- PUT /api/v1/users/{id} → 200 or 404
173
- DELETE /api/v1/users/{id} → 204 or 404
174
- GET /api/v1/users/{id}/orders → 200 nested resource
175
- ```
176
-
177
- ---
178
-
179
- ## Performance Rules
180
-
181
- - Always use pagination — never return unbounded lists
182
- - Avoid N+1: use `\@EntityGraph` or `JOIN FETCH` in JPQL
183
- - Add database indexes on frequently queried columns
184
- - Use `\@Transactional(readOnly = true)` on read-only service methods
185
- - For heavy read operations, consider projection interfaces
186
-
187
- ---
188
-
189
- ## Security Rules
190
-
191
- - Never log passwords, tokens, or sensitive PII
192
- - Hash passwords with BCrypt: `passwordEncoder.encode(rawPassword)`
193
- - Validate and sanitize all user inputs via Bean Validation
194
- - Use `\@PreAuthorize` for method-level security
195
- - Never return stack traces to API consumers
196
- - Store secrets in environment variables / Vault — never in code
197
-
198
- ---
199
-
200
- ## Logging Rules
201
-
202
- - Use SLF4J with Lombok `\@Slf4j`
203
- - `log.info` — normal business events
204
- - `log.warn` — recoverable issues (not found, validation fail)
205
- - `log.error` — unexpected exceptions (always include `ex` as the second argument)
206
- - Never log sensitive data (password, credit card, token)
207
-
208
- ---
209
-
210
- ## Common Anti-Patterns to Avoid
211
-
212
- - ❌ `\@Autowired` field injection → use constructor injection (Lombok `\@RequiredArgsConstructor`)
213
- - ❌ `\@Data` on JPA entities → use `\@Getter \@Setter` separately
214
- - ❌ Returning `Entity` directly from Controller → always use DTO
215
- - ❌ `SELECT *` or unbounded `findAll()` → always paginate
216
- - ❌ Business logic in Controller → move to Service
217
- - ❌ Catching and swallowing exceptions → handle properly or rethrow
218
- - ❌ `new RuntimeException("something")` → create a specific custom exception
219
- - ❌ Hardcoding config values → use `\@Value` or `\@ConfigurationProperties`
220
- - ❌ `\@Transactional` on Controller → only on Service methods
221
-
222
- ---
223
-
224
- When explaining changes, refer to the [Spring Boot Official Documentation](https://docs.spring.io/spring-boot/docs/current/reference/html/) and [Spring Data JPA](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/) conventions.
1
+ # Spring Boot AI System Prompt
2
+
3
+ You are an expert Java Spring Boot developer. Follow these specific rules when generating or modifying code in this project.
4
+
5
+ > **Rules & code examples:** Read `.rules/java/spring-boot-rules.md` (architecture, layer rules, naming, security, anti-patterns) and `.rules/java/spring-boot-examples.md` (code samples per layer) **in full** before writing or modifying any Java code in this project.
@@ -7,17 +7,9 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ---
9
9
 
10
- ## [0.2.2] - 2026-08-06
11
-
12
- ### Added
13
-
14
- - **`ak gate <n> skip --ticket <id> [--reason <text>]`** — a gate can now close as "skipped" (legitimately produced no deliverable) instead of only `start`/`approved`. First consumer: `ingest-data` Gate 3, when the AI decides the ingested content is reference-only and no task list is needed — it now runs `ak gate 3 skip` instead of leaving the gate untouched. `scripts/task.js`'s `updateTaskGateState()` gained a `skip` branch that records `skippedGates[gate] = {at, reason}` in `task-state.json` and advances `currentGate` exactly like `approved` does, so every existing "is this task done" check (which only ever inspects `currentGate`) keeps working with zero changes. Prompted by an impact assessment for the `ai-flow-ex` VS Code extension's dashboard, which was showing skipped-Gate-3 ingest-data tasks stuck as "ready to run Gate 3" forever — see `docs/superpowers/specs/2026-08-06-ingest-data-gate3-skipped-state-design.md` in `ai-flow-ex` for the paired UI-side change (reads `skippedGates` to render a distinct "⏭ Skipped" badge instead of the generic "✓ Approved").
15
-
16
- ### Fixed
17
-
18
- - **`ak task next` treated `ingest-data` as a 2-gate flow** — a stale assumption left over from before Gate 3 (task creation & ticket generation) was added to `ingest-data` earlier in the same release cycle. `scripts/task.js` grouped it with `gen-doc`/`create-system-requirement` (`maxGate = 2`), so pausing via `ak task next` on Gate 2 would prematurely mark the task `status: 'done'` and `gateLabel(3, 'ingest-data')` returned `'Done'` instead of `'Sinh Task & Tạo Ticket'`. Split into its own 3-gate branch (`maxGate = 3`); `gen-doc`/`create-system-requirement` are unaffected.
10
+ ## [Unreleased]
19
11
 
20
- ## [0.2.1] - 2026-08-05
12
+ ## [0.2.1] - 2026-08-17
21
13
 
22
14
  ### Added
23
15
 
@@ -30,6 +22,24 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
30
22
  - Approved tickets get their link written back into the originating `Meetings-Log`/`QnA-Log`/`Confirmations-Log` entry (`→ Tasks created: TICKET-101 (coding), ...`), keeping the traceability chain from PM Workflow §6 intact end-to-end.
31
23
  - **Still open, tracked in the PM Workflow doc's Vấn đề 5 implementation note:** no fixed feedback→task-type rule table (AI still judges per-context); new tickets don't auto-attach `functionId`/labels (Dev/BA/QA still assign on `ak use TICKET-XXX`); Backlog issueType/priority default to the project's first issueType and a "Normal"-named priority unless overridden explicitly in the task JSON.
32
24
 
25
+ - **`ak gate <n> skip --ticket <id> [--reason <text>]`** — a gate can now close as "skipped" (legitimately produced no deliverable) instead of only `start`/`approved`. First consumer: `ingest-data` Gate 3, when the AI decides the ingested content is reference-only and no task list is needed — it now runs `ak gate 3 skip` instead of leaving the gate untouched. `scripts/task.js`'s `updateTaskGateState()` gained a `skip` branch that records `skippedGates[gate] = {at, reason}` in `task-state.json` and advances `currentGate` exactly like `approved` does, so every existing "is this task done" check (which only ever inspects `currentGate`) keeps working with zero changes. Prompted by an impact assessment for the `ai-flow-ex` VS Code extension's dashboard, which was showing skipped-Gate-3 ingest-data tasks stuck as "ready to run Gate 3" forever — see `docs/superpowers/specs/2026-08-06-ingest-data-gate3-skipped-state-design.md` in `ai-flow-ex` for the paired UI-side change (reads `skippedGates` to render a distinct "⏭ Skipped" badge instead of the generic "✓ Approved").
26
+
27
+ ### Changed
28
+
29
+ - **Per-framework rule sections (Architecture, layer rules, naming, security, anti-patterns) moved out of `CLAUDE.md`/`AGENTS.md` into `.rules/<lang>/<framework>-rules.md`, alongside the existing `-examples.md`.** Extends the same on-demand `.rules/` pattern already used for code samples to the rules themselves: `custom/templates/<framework>.md` now holds only the title, a one-line intro, and a single "> **Rules & code examples:**" pointer to both files — everything else (Controller/Service/Repository/Entity/DTO/Mapper/Exception/Testing/Naming/API/Performance/Security/Logging/anti-patterns, per framework) lives in the new `-rules.md` file. Applied to all 9 templates that had this structure (`spring-boot`, `php`, `php-plain`, `reactjs`, `nestjs`, `nodejs-express`, `python`, `python-django`, `python-fastapi`, `python-ml`) — `nextjs`/`vue-nuxt`/`laravel` were already ~15 lines with no such structure and are untouched. Unlike the workflow-file pointer (read once at a clear Gate-start trigger), these rules apply continuously across every code edit in Gate 3, so `custom/templates/shared/gate-workflow.md` and `ml-gate-workflow.md` both gained an explicit bullet at the top of their Gate 3 (`Code Generation` / `Implement, Train & Iterate`) step: read the rules + examples files pointed to at the top of `CLAUDE.md`/`AGENTS.md` before the first line of code, and keep applying them to every subsequent edit, not just the first. Also fixed `generateSkillNameList()`'s "Codex indexes these automatically" wording, which became inaccurate once `claude` also uses the compact skill list (see previous entry) — reworded to name neither tool specifically. Measured on the kit's own dogfood skill set: solo `spring-boot`/`reactjs`/`php` 247/211/~205 → **41 lines each**; multi `spring-boot`+`php` 385 → **55 lines**.
30
+
31
+ - **`CLAUDE.md` now uses the same compact skill list as `AGENTS.md`, and 2 techstack templates dropped a redundant "Project Stack" section.** `AI_TOOL_SKILL_LIST_STYLE` gained `'claude': 'compact'` — Claude Code auto-discovers each skill's name + description from `.claude/skills/*/SKILL.md` frontmatter on its own (same mechanism the original Codex-only rationale assumed only Codex had), so restating the full description table in `CLAUDE.md` was duplicating information the tool already surfaces natively. `custom/templates/spring-boot.md` and `reactjs.md` also dropped their "## Project Stack" bullet list (Java/Spring Boot/Maven version pins, React/TypeScript/Vite/Router/etc.) — for spring-boot every item was already restated in a more specific rule section further down (Testing Rules, Entity Rules, Mapper Rules...); for reactjs most items were too (State Management, Form, Styling, API Layer Rules), except **Router (React Router v6), the shadcn/ui pairing with Tailwind, and Vitest as the specific test runner**, which had no other home in the file and are now gone rather than relocated — worth restoring into an existing rule section later if that specificity is missed. Measured on the kit's own dogfood skill set (~46 skills): solo `spring-boot` 306 → 247 lines, solo `reactjs` ~264 → 211 lines, multi `spring-boot`+`php` 444 → 385 lines.
32
+
33
+ - **`CLAUDE.md` now uses the pointer layout too, not just `AGENTS.md`.** `AI_TOOL_LAYOUT` in `scripts/init.js` gained `'claude': 'pointer'` alongside the existing `'codex': 'pointer'` — CLAUDE.md is loaded in full at the start of every session regardless of which gate (if any) is active, so inlining the ~115 KB DEV/TESTER/EXECUTE/gen-doc/ingest-data + BA + QA workflow set spent context on gates most sessions never touch. The workflow bodies now live in `.aiflow/instructions/` and CLAUDE.md just links to them, same as Codex. Unlike Codex's pointer layout, the skill registry keeps its full description table for `claude` (`resolveSkillListStyle()`/`resolveSkillsRoot()`) — Claude Code has no skill index of its own the way Codex does, so only the workflow bodies move out, not the registry.
34
+ - **Techstack rule templates (`custom/templates/<framework>.md`) no longer inline full code samples for every layer.** Following the pattern already used for `spring-boot.md`/`custom/rules/java/spring-boot-examples.md`, the same split was applied to the other 9 templates that had grown a "✅ Good / ❌ Bad" code block per rule area: `reactjs`, `php`, `php-plain`, `python-ml`, `nestjs`, `nodejs-express`, `python`, `python-django`, `python-fastapi`. Each template keeps its bullet rules, naming table, and anti-pattern list, with a one-line pointer to a new `custom/rules/<lang>/<framework>-examples.md` file holding the actual code samples — read on demand when generating code for that specific layer instead of being inlined into every session's context. `FRAMEWORK_LANGUAGE` in `scripts/init.js` also gained entries for `nodejs-express`, `python`, `python-django`, `python-fastapi`, and `php-plain` (previously missing, so `.rules/<lang>/` was never copied into projects using those frameworks).
35
+ - **Multi-framework projects no longer duplicate the tool header, skill registry, and workflow-files pointer once per framework.** A project with 2+ frameworks (e.g. a `spring-boot` backend + a `reactjs` frontend) calls `setupFramework()` once per framework with `multi: true`; each call used to emit a fully self-contained block (tool header + full skill registry + framework rules + "Installed workflow files" pointer note), so all three non-framework-specific sections got repeated verbatim per framework — the ~46-skill registry was the dominant cost in `CLAUDE.md` once the per-framework code samples above were split out. `setupFramework()` now builds all three via `buildSharedBlock()` once and writes it under its own fixed `<!-- aiflow-kit-start:shared -->` marker; per-framework blocks (`buildToolBlock(tool, ..., { includeShared: false })`) carry only the framework-specific rules and (for inline tools) the inlined gate-workflow body. The "Installed workflow files" note moved to the shared block because its 3 listed paths (`.aiflow/instructions/gate-workflow.md`, `create-spec-workflow.md`, `create-testcase-workflow.md`) are always the same 3 filenames regardless of framework — only their on-disk *content* varies per framework (e.g. ML vs standard gate workflow), not the pointer text itself. Re-running for an already-present framework upserts its block in place (new `upsertMarkerBlock()` helper) instead of appending another copy. Single-framework projects are unaffected (`includeShared` defaults to `true`, same single block as before). Measured on the kit's own dogfood project (spring-boot + php, ~46 real skills): 452 → 444 lines.
36
+
37
+ - **QnA-Log table (`skill-ba-qna-template-v1.md`) gained 3 response-tracking columns — Người trả lời, Ngày trả lời, Nguồn.** `skill-ba-qna-v1.md`'s Bước 4 now spells out how each is filled: **Người trả lời** defaults to `git config user.email` (fallback `user.name`) of whoever is running the session, overridden only when the BA explicitly names a different responder (e.g. answering on the customer's behalf); **Ngày trả lời** is stamped automatically from the current system date (`dd/mm/yyyy`), never asked; **Nguồn** is always BA-entered manually (e.g. "Khách hàng", "Nội bộ team", "Team Marketing") with no default or inference. Questions still **Open** leave all three columns blank (`—`). Checklist gained a matching item to catch missing values on **Confirmed** rows.
38
+
39
+ ### Fixed
40
+
41
+ - **`ak task next` treated `ingest-data` as a 2-gate flow** — a stale assumption left over from before Gate 3 (task creation & ticket generation) was added to `ingest-data` earlier in the same release cycle. `scripts/task.js` grouped it with `gen-doc`/`create-system-requirement` (`maxGate = 2`), so pausing via `ak task next` on Gate 2 would prematurely mark the task `status: 'done'` and `gateLabel(3, 'ingest-data')` returned `'Done'` instead of `'Sinh Task & Tạo Ticket'`. Split into its own 3-gate branch (`maxGate = 3`); `gen-doc`/`create-system-requirement` are unaffected.
42
+
33
43
  ## [0.2.0] - 2026-08-04
34
44
 
35
45
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relipa/ai-flow-kit",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "All-in-one AI Flow Kit for team development with Claude AI - skills, templates, and MCP adapters",
5
5
  "author": "Example Team",
6
6
  "publishConfig": {
package/scripts/init.js CHANGED
@@ -25,14 +25,19 @@ function commandExists(cmd) {
25
25
 
26
26
  // Map framework → language family for picking the right rules
27
27
  const FRAMEWORK_LANGUAGE = {
28
- 'laravel': 'php',
29
- 'php': 'php',
30
- 'spring-boot': 'java',
31
- 'reactjs': 'javascript',
32
- 'nextjs': 'javascript',
33
- 'vue-nuxt': 'javascript',
34
- 'nestjs': 'javascript',
35
- 'python-ml': 'python',
28
+ 'laravel': 'php',
29
+ 'php': 'php',
30
+ 'php-plain': 'php',
31
+ 'spring-boot': 'java',
32
+ 'reactjs': 'javascript',
33
+ 'nextjs': 'javascript',
34
+ 'vue-nuxt': 'javascript',
35
+ 'nestjs': 'javascript',
36
+ 'nodejs-express': 'javascript',
37
+ 'python-ml': 'python',
38
+ 'python': 'python',
39
+ 'python-django': 'python',
40
+ 'python-fastapi': 'python',
36
41
  };
37
42
 
38
43
  // Frameworks that use the ML-specific gate workflow instead of the default one
@@ -69,8 +74,16 @@ const AI_TOOL_FILES = {
69
74
  // Codex is 'pointer' because it caps the combined AGENTS.md chain at
70
75
  // project_doc_max_bytes (32 KiB default) and silently drops whatever exceeds it.
71
76
  // The assembled workflow set is ~115 KB, so inlining would truncate it mid-gate.
77
+ //
78
+ // Claude is 'pointer' for a softer reason: no hard byte cap, but CLAUDE.md is
79
+ // loaded in full at the start of every session regardless of which gate (or none)
80
+ // is actually active, so inlining ~115 KB of DEV/TESTER/EXECUTE/gen-doc/ingest-data
81
+ // + BA + QA workflow text spends context on gates most sessions never touch.
82
+ // Claude Code already reads files on demand via Read/Skill, so a short pointer
83
+ // works the same way a Skill reference does.
72
84
  const AI_TOOL_LAYOUT = {
73
85
  'codex': 'pointer',
86
+ 'claude': 'pointer',
74
87
  };
75
88
 
76
89
  /** Return 'inline' or 'pointer' for a tool key. */
@@ -78,6 +91,39 @@ function resolveToolLayout(tool) {
78
91
  return AI_TOOL_LAYOUT[tool] || 'inline';
79
92
  }
80
93
 
94
+ // Skill-list rendering per tool.
95
+ // 'full' — verbose markdown table with description per skill (default, for
96
+ // tools with no native skill index — cursor/gemini/copilot/generic).
97
+ // 'compact' — bare skill-name list.
98
+ //
99
+ // Both Codex (.codex/skills/) and Claude Code (.claude/skills/) auto-discover
100
+ // name + description from each SKILL.md's frontmatter on their own, so restating
101
+ // the full table in the generated instruction file just burns context on every
102
+ // turn for information the tool already surfaces natively. The compact list still
103
+ // tells the AI which skill names exist and where to `Read` the full SKILL.md when
104
+ // a workflow step names one — it just stops duplicating the description.
105
+ const AI_TOOL_SKILL_LIST_STYLE = {
106
+ 'codex': 'compact',
107
+ 'claude': 'compact',
108
+ };
109
+
110
+ /** Return 'full' or 'compact' skill-list style for a tool key. */
111
+ function resolveSkillListStyle(tool) {
112
+ return AI_TOOL_SKILL_LIST_STYLE[tool] || 'full';
113
+ }
114
+
115
+ // Where each tool actually reads its skills from, for re-rooting paths in the
116
+ // generated registry/name-list. Only Codex mirrors skills into its own directory
117
+ // (.codex/skills/); every other tool reads the canonical .claude/skills/ directly.
118
+ const AI_TOOL_SKILLS_ROOT = {
119
+ 'codex': '.codex/skills',
120
+ };
121
+
122
+ /** Return the skills-root path to embed in a tool's generated instruction file. */
123
+ function resolveSkillsRoot(tool) {
124
+ return AI_TOOL_SKILLS_ROOT[tool] || '.claude/skills';
125
+ }
126
+
81
127
  // Shared workflow templates copied to .aiflow/instructions/ for pointer-layout tools.
82
128
  // Keys are the destination basenames; gate workflow resolves per framework.
83
129
  const WORKFLOW_INSTRUCTION_FILES = [
@@ -612,7 +658,8 @@ async function generateSkillNameList(projectDir, skillsRoot = '.codex/skills') {
612
658
  if (!names.length) return '';
613
659
 
614
660
  let out = `## Available skills\n\n`;
615
- out += `Codex indexes these automatically from \`${skillsRoot}/\`. When a workflow step says `;
661
+ out += `These skills are indexed automatically from \`${skillsRoot}/\` (name + description come from `;
662
+ out += `each SKILL.md's frontmatter). When a workflow step says `;
616
663
  out += `**INVOKE:** \`<name>\`, read \`${skillsRoot}/<name>/SKILL.md\` in full and follow it:\n\n`;
617
664
  out += names.sort().map(n => `\`${n}\``).join(' · ') + '\n';
618
665
  return out;
@@ -653,6 +700,29 @@ async function generateSkillRegistry(projectDir, skillsRoot = '.claude/skills')
653
700
  return registry;
654
701
  }
655
702
 
703
+ /**
704
+ * Write `finalContent` into `targetPath` under its own marker pair: replace the
705
+ * existing block in place if the markers are already present, append (separated
706
+ * by `sep`) if the file exists without them, or create the file fresh otherwise.
707
+ * Used for multi-framework projects, where several independent markers
708
+ * (one per framework, plus the shared tool/registry block) share one file.
709
+ */
710
+ async function upsertMarkerBlock(targetPath, markerStart, markerEnd, finalContent, sep) {
711
+ const exists = await fs.pathExists(targetPath);
712
+ if (!exists) {
713
+ await fs.writeFile(targetPath, finalContent);
714
+ return;
715
+ }
716
+ const existingContent = await fs.readFile(targetPath, 'utf-8');
717
+ const hasBlock = existingContent.includes(markerStart) && existingContent.includes(markerEnd);
718
+ if (hasBlock) {
719
+ const regex = new RegExp(`${markerStart}[\\s\\S]*?${markerEnd}`, 'g');
720
+ await fs.writeFile(targetPath, existingContent.replace(regex, finalContent));
721
+ } else {
722
+ await fs.appendFile(targetPath, sep + finalContent);
723
+ }
724
+ }
725
+
656
726
  async function setupFramework(projectDir, framework, multi = false, selectedTools = Object.keys(AI_TOOL_FILES), options = {}) {
657
727
  if (!framework) return;
658
728
  const frameworkTemplatePath = path.join(PKG_DIR, 'custom', 'templates', `${framework}.md`);
@@ -683,12 +753,19 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
683
753
  const workflowContent = [gateWorkflowContent, createSpecContent, createTestcaseContent].filter(Boolean).join('\n\n---\n\n');
684
754
 
685
755
  const separator = `\n\n---\n\n`;
686
- const skillRegistry = await generateSkillRegistry(projectDir);
687
- const codexSkillList = await generateSkillNameList(projectDir, '.codex/skills');
688
756
 
689
757
  const markerStart = '<!-- aiflow-kit-start -->';
690
758
  const markerEnd = '<!-- aiflow-kit-end -->';
691
759
 
760
+ // Multi-framework projects get exactly one shared block per tool (tool header +
761
+ // skill registry) instead of one copy per framework block — neither depends on
762
+ // which framework triggered this call, so repeating them per framework was pure
763
+ // duplication (e.g. spring-boot + php in the same project doubled the ~44-skill
764
+ // registry). Re-running for a second framework upserts this block in place via
765
+ // its fixed markers rather than appending another copy.
766
+ const sharedMarkerStart = '<!-- aiflow-kit-start:shared -->';
767
+ const sharedMarkerEnd = '<!-- aiflow-kit-end:shared -->';
768
+
692
769
  // Pointer-layout tools need the workflows on disk instead of inlined.
693
770
  const needsPointer = selectedTools.some(t => resolveToolLayout(t) === 'pointer');
694
771
  const instructionPaths = needsPointer
@@ -696,12 +773,13 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
696
773
  : [];
697
774
 
698
775
  /**
699
- * Assemble the marker block for one tool.
700
- * 'inline' tools get the whole workflow set embedded; 'pointer' tools get a
701
- * short "installed workflow files" list and read the workflows from disk.
776
+ * Tool header + skill registry + (for pointer tools) the "installed workflow
777
+ * files" pointer note none of this depends on which framework is active:
778
+ * the 3 instruction-file paths are always the same 3 filenames regardless of
779
+ * framework (only their on-disk *content* varies), so this used to be
780
+ * duplicated per framework block for no reason.
702
781
  */
703
- async function buildToolBlock(tool, blockMarkerStart, blockMarkerEnd) {
704
- const pointer = resolveToolLayout(tool) === 'pointer';
782
+ async function buildSharedBlock(tool) {
705
783
  let body = '';
706
784
 
707
785
  const toolTemplatePath = path.join(PKG_DIR, 'custom', 'templates', 'tools', `${tool}.md`);
@@ -709,19 +787,46 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
709
787
  body += await fs.readFile(toolTemplatePath, 'utf-8') + '\n\n';
710
788
  }
711
789
 
712
- if (frameworkContent) body += frameworkContent + '\n\n';
713
-
714
- const registry = pointer ? codexSkillList : skillRegistry;
790
+ const skillsRoot = resolveSkillsRoot(tool);
791
+ const registry = resolveSkillListStyle(tool) === 'compact'
792
+ ? await generateSkillNameList(projectDir, skillsRoot)
793
+ : await generateSkillRegistry(projectDir, skillsRoot);
715
794
  if (registry) body += registry + '\n\n';
716
795
 
717
- if (pointer) {
718
- if (instructionPaths.length) {
719
- body += `## Installed workflow files\n\n`;
720
- body += `Generated for framework \`${framework}\` by ai-flow-kit v${PKG_VERSION}. `;
721
- body += `Read the relevant file in full before starting a gate:\n\n`;
722
- body += instructionPaths.map(p => `- \`${p}\``).join('\n') + '\n\n';
723
- }
724
- } else if (workflowContent) {
796
+ const pointer = resolveToolLayout(tool) === 'pointer';
797
+ if (pointer && instructionPaths.length) {
798
+ body += `## Installed workflow files\n\n`;
799
+ body += `Generated by ai-flow-kit v${PKG_VERSION}. `;
800
+ body += `These are the full DEV/TESTER/EXECUTE (+ gen-doc/ingest-data), BA spec, and QA `;
801
+ body += `testcase gate workflows — too large to inline here. Read the relevant file `;
802
+ body += `**in full** before starting a gate; do not work from a summary and do not skip, `;
803
+ body += `shorten, or merge gates:\n\n`;
804
+ body += instructionPaths.map(p => `- \`${p}\``).join('\n');
805
+ }
806
+
807
+ return body.trim();
808
+ }
809
+
810
+ /**
811
+ * Assemble the marker block for one tool.
812
+ * 'inline' tools get the framework's gate workflow embedded inline; 'pointer'
813
+ * tools already got their "read from disk" note via buildSharedBlock() above.
814
+ * Pass includeShared: false for multi-framework projects — the tool header,
815
+ * skill registry, and workflow-files note are written once via
816
+ * buildSharedBlock() instead.
817
+ */
818
+ async function buildToolBlock(tool, blockMarkerStart, blockMarkerEnd, { includeShared = true } = {}) {
819
+ const pointer = resolveToolLayout(tool) === 'pointer';
820
+ let body = '';
821
+
822
+ if (includeShared) {
823
+ const shared = await buildSharedBlock(tool);
824
+ if (shared) body += shared + '\n\n';
825
+ }
826
+
827
+ if (frameworkContent) body += frameworkContent + '\n\n';
828
+
829
+ if (!pointer && workflowContent) {
725
830
  body += workflowContent + '\n\n';
726
831
  }
727
832
 
@@ -802,7 +907,7 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
802
907
  const blockMarkerStart = multi ? `<!-- aiflow-kit-start:${framework} -->` : markerStart;
803
908
  const blockMarkerEnd = multi ? `<!-- aiflow-kit-end:${framework} -->` : markerEnd;
804
909
 
805
- const finalContent = await buildToolBlock(tool, blockMarkerStart, blockMarkerEnd);
910
+ const finalContent = await buildToolBlock(tool, blockMarkerStart, blockMarkerEnd, { includeShared: !multi });
806
911
 
807
912
  const fileExists = await fs.pathExists(targetPath);
808
913
 
@@ -861,19 +966,15 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
861
966
  }
862
967
  }
863
968
  } else {
864
- if (fileExists) {
865
- const existingContent = await fs.readFile(targetPath, 'utf-8');
866
- const hasBlock = existingContent.includes(blockMarkerStart) && existingContent.includes(blockMarkerEnd);
867
- if (hasBlock) {
868
- const regex = new RegExp(`${blockMarkerStart}[\\s\\S]*?${blockMarkerEnd}`, 'g');
869
- const updatedContent = existingContent.replace(regex, finalContent);
870
- await fs.writeFile(targetPath, updatedContent);
871
- } else {
872
- await fs.appendFile(targetPath, separator + finalContent);
873
- }
874
- } else {
875
- await fs.writeFile(targetPath, finalContent);
969
+ // Upsert the shared block (tool header + skill registry) once per tool —
970
+ // fixed markers mean a second framework's call replaces it in place
971
+ // instead of appending another copy.
972
+ const sharedBody = await buildSharedBlock(tool);
973
+ if (sharedBody) {
974
+ const sharedFinal = `${sharedMarkerStart}\n${sharedBody}\n${sharedMarkerEnd}`;
975
+ await upsertMarkerBlock(targetPath, sharedMarkerStart, sharedMarkerEnd, sharedFinal, separator);
876
976
  }
977
+ await upsertMarkerBlock(targetPath, blockMarkerStart, blockMarkerEnd, finalContent, separator);
877
978
  written.push(AI_TOOL_FILES[tool]);
878
979
  }
879
980
  }
@@ -1523,6 +1624,8 @@ module.exports = init;
1523
1624
  module.exports.AI_TOOL_FILES = AI_TOOL_FILES;
1524
1625
  module.exports.AI_TOOL_LAYOUT = AI_TOOL_LAYOUT;
1525
1626
  module.exports.resolveToolLayout = resolveToolLayout;
1627
+ module.exports.resolveSkillListStyle = resolveSkillListStyle;
1628
+ module.exports.resolveSkillsRoot = resolveSkillsRoot;
1526
1629
  module.exports.writeWorkflowInstructions = writeWorkflowInstructions;
1527
1630
  module.exports.setupCodex = setupCodex;
1528
1631
  module.exports.setupCodexSkills = setupCodexSkills;