@relipa/ai-flow-kit 0.0.6 → 0.0.7-beta.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 (40) hide show
  1. package/README.md +41 -16
  2. package/bin/aiflow.js +65 -2
  3. package/custom/mcp-presets/gitnexus.json +8 -0
  4. package/custom/skills/generate-spec/SKILL.md +7 -0
  5. package/custom/skills/impact-analysis/SKILL.md +10 -0
  6. package/custom/skills/investigate-bug/SKILL.md +17 -0
  7. package/custom/skills/read-study-requirement/SKILL.md +11 -0
  8. package/custom/skills/review-plan/SKILL.md +15 -0
  9. package/custom/templates/php-plain.md +261 -0
  10. package/docs/common/AIFLOW.md +17 -6
  11. package/docs/common/CHANGELOG.md +63 -5
  12. package/docs/common/QUICK_START.md +33 -13
  13. package/docs/common/cli-reference.md +23 -0
  14. package/package.json +2 -7
  15. package/scripts/checkpoint.js +46 -0
  16. package/scripts/doctor.js +111 -8
  17. package/scripts/gitnexus-worker.js +94 -0
  18. package/scripts/guide.js +42 -51
  19. package/scripts/hooks/session-start.js +35 -5
  20. package/scripts/hooks/session-stop.js +55 -0
  21. package/scripts/init.js +299 -19
  22. package/scripts/prompt.js +2 -2
  23. package/scripts/remove.js +54 -0
  24. package/scripts/task.js +101 -0
  25. package/scripts/update.js +14 -4
  26. package/scripts/use.js +78 -6
  27. package/upstream/.claude-plugin/marketplace.json +4 -4
  28. package/upstream/.claude-plugin/plugin.json +2 -4
  29. package/upstream/.cursor-plugin/plugin.json +2 -4
  30. package/upstream/docs/plans/2025-11-22-opencode-support-design.md +1 -1
  31. package/upstream/docs/plans/2025-11-28-skills-improvements-from-user-feedback.md +2 -2
  32. package/upstream/docs/testing.md +2 -2
  33. package/upstream/skills/subagent-driven-development/SKILL.md +5 -6
  34. package/upstream/skills/subagent-driven-development/implementer-prompt.md +2 -3
  35. package/upstream/skills/systematic-debugging/CREATION-LOG.md +1 -1
  36. package/upstream/skills/systematic-debugging/root-cause-tracing.md +1 -1
  37. package/upstream/skills/tdd-lean/SKILL.md +127 -0
  38. package/upstream/skills/using-git-worktrees/SKILL.md +4 -5
  39. package/upstream/skills/writing-plans/SKILL.md +3 -9
  40. package/upstream/tests/brainstorm-server/package-lock.json +36 -0
package/README.md CHANGED
@@ -16,18 +16,18 @@ Developers only need a single command to load ticket context → AI automaticall
16
16
  | New plugins (superpowers, MCP...) require manual setup | Package auto-sets up hooks, skills, and MCP on init |
17
17
  | Missing investigation / bug reproduction flow | Skill `investigate-bug` (inherits systematic-debugging) |
18
18
  | Missing impact analysis flow | Skill `impact-analysis` |
19
- | Hard to resume tasks | **State Resumption**: Unified `plan/` docs and context across tools |
19
+ | Hard to resume tasks | **State Resumption**: Unified `plan/` docs, `task-summary.md` progress tracking, and intelligent session continuity (automatic Gate 3 resumption). |
20
20
 
21
21
  ## Documentation & Guides
22
22
 
23
23
  > [!TIP]
24
24
  > After running `aiflow init`, documentation files are automatically copied to `.aiflow/docs/` in your project for easy access.
25
25
 
26
- - **[Quick Start Guide](https://gitlab.relipa.vn/ai/ai-flow-kit/-/blob/main/docs/common/QUICK_START.md)** — Step-by-step instructions for developers.
27
- - **[Full Workflow (5 Gates)](https://gitlab.relipa.vn/ai/ai-flow-kit/-/blob/main/docs/common/AIFLOW.md)** — In-depth look at the Gate process.
28
- - **[Troubleshooting](https://gitlab.relipa.vn/ai/ai-flow-kit/-/blob/main/docs/common/troubleshooting.md)** — Common issues and fixes.
29
- - **[Integration Guide](https://gitlab.relipa.vn/ai/ai-flow-kit/-/blob/main/docs/common/ai-integration.md)** — Advanced tool setup (Claude, Cursor, Gemini).
30
- - **[Changelog](https://gitlab.relipa.vn/ai/ai-flow-kit/-/blob/main/docs/common/CHANGELOG.md)** — Version history and release notes.
26
+ - **[Quick Start Guide](https://github.com/example/ai-flow-kit/blob/main/docs/common/QUICK_START.md)** — Step-by-step instructions for developers.
27
+ - **[Full Workflow (5 Gates)](https://github.com/example/ai-flow-kit/blob/main/docs/common/AIFLOW.md)** — In-depth look at the Gate process.
28
+ - **[Troubleshooting](https://github.com/example/ai-flow-kit/blob/main/docs/common/troubleshooting.md)** — Common issues and fixes.
29
+ - **[Integration Guide](https://github.com/example/ai-flow-kit/blob/main/docs/common/ai-integration.md)** — Advanced tool setup (Claude, Cursor, Gemini).
30
+ - **[Changelog](https://github.com/example/ai-flow-kit/blob/main/docs/common/CHANGELOG.md)** — Version history and release notes.
31
31
 
32
32
  ---
33
33
 
@@ -52,7 +52,7 @@ PM writes ticket on Backlog/Jira
52
52
 
53
53
  ⛩️ GATE 3 — Code Generation (TDD) [AI]
54
54
  AI writes tests first → implement → tests pass
55
- Commit each small step
55
+ Verify implementation works
56
56
  │ Code done
57
57
 
58
58
  ⛩️ GATE 4 — AI Self-Review [AI + DEV]
@@ -74,13 +74,13 @@ PM writes ticket on Backlog/Jira
74
74
 
75
75
  ```bash
76
76
  # Stable version
77
- npm install -g @relipa/ai-flow-kit
77
+ npm install -g ai-flow-kit
78
78
 
79
79
  # Beta version (recommend for newest features)
80
- npm install -g @relipa/ai-flow-kit@beta
80
+ npm install -g ai-flow-kit@beta
81
81
 
82
82
  # Uninstall
83
- npm uninstall -g @relipa/ai-flow-kit
83
+ npm uninstall -g ai-flow-kit
84
84
  ```
85
85
 
86
86
  ---
@@ -93,6 +93,7 @@ npm uninstall -g @relipa/ai-flow-kit
93
93
  | `aiflow use <ticket>` | Load ticket context (Backlog/Jira). |
94
94
  | `aiflow task` | Manage multiple tasks — pause, switch, resume. |
95
95
  | `aiflow prompt` | Generate tool-optimized prompts (`--env cursor`). |
96
+ | `aiflow sync-skills` | Manually synchronize AI Instruction files with local custom skills. |
96
97
  | `aiflow telemetry` | Manage telemetry tracking (enable/disable/status). |
97
98
  | `aiflow guide` | View integrated multi-AI integration guide. |
98
99
 
@@ -104,8 +105,10 @@ Initial setup for the project. Run once per project.
104
105
  # Single framework + adapter
105
106
  aiflow init --framework spring-boot --adapter backlog
106
107
 
107
- # Force enable RTK token compression
108
- aiflow init --with-rtk
108
+ # Enable token savings tools
109
+ aiflow init --with-rtk # RTK: compress bash outputs (60–90% reduction)
110
+ # aiflow init --with-gitnexus # GitNexus: code intelligence (waits for index)
111
+ # aiflow init --with-gitnexus --no-wait # GitNexus: index in background
109
112
 
110
113
  # Multiple frameworks and adapters
111
114
  aiflow init --framework spring-boot,reactjs --adapter backlog,jira
@@ -120,12 +123,27 @@ aiflow init --framework spring-boot,reactjs --adapter backlog,jira
120
123
  - `.mcp.json` + Claude Desktop config — MCP adapter
121
124
  - `.aiflow/credentials.json` — save API credentials
122
125
 
123
- **Supported Frameworks:** `spring-boot`, `laravel`, `reactjs`, `nextjs`, `vue-nuxt`
126
+ **Supported Frameworks:** `spring-boot`, `laravel`, `php-plain`, `nestjs`, `reactjs`, `nextjs`, `vue-nuxt`, `nodejs-express`, `python-django`, `python-fastapi`
124
127
 
125
128
  **Supported Adapters:** `backlog`, `jira`, `google-sheets`, `figma`
126
129
 
127
130
  ---
128
131
 
132
+ ### `aiflow sync-skills`
133
+
134
+ Synchronize AI instruction files (`CLAUDE.md`, `GEMINI.md`, etc.) and Skills without upgrading the package version.
135
+
136
+ ```bash
137
+ aiflow sync-skills
138
+ ```
139
+
140
+ **Features:**
141
+ - Intelligent marker-based updates (`<!-- aiflow-kit-start -->`)
142
+ - Confirmation prompts before overwriting custom rules
143
+ - English notifications and status reports
144
+
145
+ ---
146
+
129
147
  ### `aiflow use <ticket>`
130
148
 
131
149
  Load ticket context into `.aiflow/context/current.json`.
@@ -154,7 +172,7 @@ aiflow use PROJ-33 --comments-from 3
154
172
  aiflow use --manual
155
173
 
156
174
  # Load from local file (JSON or plain text)
157
- aiflow use --file task.md
175
+ aiflow use --file task.md # Auto-generates taskId from filename & prompts for taskType
158
176
 
159
177
  # Modes (Fast is default)
160
178
  aiflow use PROJ-33 # Fast Mode: skip Q&A, target < 5 min
@@ -179,6 +197,8 @@ aiflow task switch PROJ-99 # pause current + switch to another task
179
197
  aiflow task resume PROJ-33 # resume a paused task (restores context + gate state)
180
198
  aiflow task reset PROJ-33 # reset to Gate 1 (keeps ticket context, clears progress)
181
199
  aiflow task remove PROJ-33 # permanently delete all saved data for the task
200
+ aiflow task next # approve current gate + prepare for fresh session
201
+ aiflow task next --ticket PROJ-33 # same, but specify ticket explicitly
182
202
  ```
183
203
 
184
204
  **Use case — switching tasks mid-flow:**
@@ -285,9 +305,14 @@ aiflow memory list
285
305
  |-----------|---------|--------------|
286
306
  | `spring-boot` | Java 17+ | `rules/java/` |
287
307
  | `laravel` | PHP 8.1+ | `rules/php/` |
308
+ | `php-plain` | PHP 8.1+ (no framework) | `rules/php/` |
309
+ | `nestjs` | TypeScript | `rules/javascript/` |
288
310
  | `reactjs` | TypeScript | `rules/javascript/` |
289
311
  | `nextjs` | TypeScript | `rules/javascript/` |
290
312
  | `vue-nuxt` | TypeScript | `rules/javascript/` |
313
+ | `nodejs-express` | JavaScript/TypeScript | `rules/javascript/` |
314
+ | `python-django` | Python | — |
315
+ | `python-fastapi` | Python | — |
291
316
 
292
317
  ---
293
318
 
@@ -337,7 +362,7 @@ ai-flow-kit/
337
362
 
338
363
  **Custom skill:** create `custom/skills/<name>/SKILL.md`
339
364
 
340
- Then run `aiflow update` to apply.
365
+ Then run `aiflow update` or `aiflow sync-skills` to apply.
341
366
 
342
367
  ---
343
368
 
@@ -355,7 +380,7 @@ Then run `aiflow update` to apply.
355
380
 
356
381
  **`aiflow` not found:**
357
382
  ```bash
358
- npm install -g @relipa/ai-flow-kit
383
+ npm install -g ai-flow-kit
359
384
  ```
360
385
 
361
386
  **Backlog load fails:**
package/bin/aiflow.js CHANGED
@@ -16,6 +16,7 @@ const removeCommand = require('../scripts/remove');
16
16
  const guideCommand = require('../scripts/guide');
17
17
  const telemetryCommand= require('../scripts/telemetry/cli');
18
18
  const taskCommand = require('../scripts/task');
19
+ const checkpointCommand = require('../scripts/checkpoint');
19
20
  const { record } = require('../scripts/telemetry/record');
20
21
 
21
22
  program
@@ -63,6 +64,8 @@ program
63
64
  .option('-e, --env <types>', 'AI environment(s)/tool(s), comma-separated (e.g. cursor,gemini,copilot)')
64
65
  .option('--with-rtk', 'force enable RTK token compression hook')
65
66
  .option('--no-rtk', 'skip RTK setup even if RTK is detected')
67
+ // .option('--with-gitnexus', 'enable GitNexus code intelligence (indexes in background)')
68
+ // .option('--wait', 'wait for GitNexus indexing to complete before exiting')
66
69
  .action((options) => {
67
70
  options.frameworks = options.framework
68
71
  ? options.framework.split(',').map(s => s.trim()).filter(Boolean)
@@ -162,6 +165,15 @@ taskCmd
162
165
  .description('Permanently delete all saved data for a task')
163
166
  .action((ticketId) => { record('command.invoked', { command: 'task.remove' }); taskCommand('remove', { taskId: ticketId }); });
164
167
 
168
+ taskCmd
169
+ .command('next')
170
+ .description('Approve current gate and prepare next session (saves state for resume)')
171
+ .option('-t, --ticket <id>', 'ticket ID (defaults to active task)')
172
+ .action((opts) => {
173
+ record('command.invoked', { command: 'task.next' });
174
+ taskCommand('next', { taskId: opts.ticket });
175
+ });
176
+
165
177
  // ── context ───────────────────────────────────────────────────
166
178
  program
167
179
  .command('context [action]')
@@ -176,6 +188,19 @@ program
176
188
  contextCommand(action, options);
177
189
  });
178
190
 
191
+ // ── checkpoint ────────────────────────────────────────────────
192
+ program
193
+ .command('checkpoint')
194
+ .description('Record token usage checkpoint (called by AI during gate work)')
195
+ .requiredOption('--gate <n>', 'gate number (1–5)', parseInt)
196
+ .requiredOption('--step <name>', 'step name (e.g. "tests-written")')
197
+ .requiredOption('--tokens <n>', 'estimated token count', parseInt)
198
+ .option('-t, --ticket <id>', 'ticket ID (defaults to active task)')
199
+ .action((opts) => {
200
+ record('command.invoked', { command: 'checkpoint' });
201
+ checkpointCommand({ gate: opts.gate, step: opts.step, tokens: opts.tokens, ticket: opts.ticket });
202
+ });
203
+
179
204
  // ── validate ──────────────────────────────────────────────────
180
205
  program
181
206
  .command('validate <file>')
@@ -253,6 +278,13 @@ program
253
278
  command: `gate${gateNum}.${action}`,
254
279
  ai_tool: options.aiTool || ''
255
280
  });
281
+
282
+ if (action === 'approved') {
283
+ console.log(chalk.cyan(`\n Gate ${gateNum} approved!`));
284
+ console.log(chalk.yellow(` Pro-Tip: To avoid context pollution, it's recommended to start a fresh chat session.`));
285
+ console.log(chalk.gray(` Run: aiflow task next${options.ticket ? ` --ticket ${options.ticket}` : ''} to save progress,`));
286
+ console.log(chalk.gray(` then open a NEW chatbox and type "continue".\n`));
287
+ }
256
288
  });
257
289
 
258
290
  // ── remove ────────────────────────────────────────────────────
@@ -276,14 +308,45 @@ program
276
308
  updateCommand(options);
277
309
  });
278
310
 
311
+ // ── sync-skills ───────────────────────────────────────────────
312
+ program
313
+ .command('sync-skills')
314
+ .description('Manually synchronize AI Instruction files with local custom skills')
315
+ .action(async () => {
316
+ record('command.invoked', { command: 'sync-skills' });
317
+ const projectDir = process.cwd();
318
+ const { setupFramework, AI_TOOL_FILES, ensureAiflowGitignored } = require('../scripts/init');
319
+ const fs = require('fs-extra');
320
+ const path = require('path');
321
+ const chalk = require('chalk');
322
+
323
+ const stateFile = path.join(projectDir, '.aiflow', 'state.json');
324
+ if (!(await fs.pathExists(stateFile))) {
325
+ console.log(chalk.red('Project is not initialized. Please run `aiflow init` first.'));
326
+ return;
327
+ }
328
+
329
+ const state = await fs.readJson(stateFile);
330
+ const frameworks = state.frameworks || [];
331
+ const selectedTools = state.aiTools || Object.keys(AI_TOOL_FILES);
332
+
333
+ console.log(chalk.cyan('⟳ Syncing AI Instruction files...'));
334
+ for (const fw of frameworks) {
335
+ await setupFramework(projectDir, fw, frameworks.length > 1, selectedTools);
336
+ }
337
+ await ensureAiflowGitignored(projectDir);
338
+ console.log(chalk.green('✨ Sync completed!'));
339
+ });
340
+
279
341
  // ── doctor ────────────────────────────────────────────────────
280
342
  program
281
343
  .command('doctor')
282
344
  .description('Health check of AI Flow Kit setup')
345
+ .option('-t, --ticket <id>', 'show token breakdown for specific ticket')
283
346
  .option('-v, --verbose', 'detailed output')
284
- .action(() => {
347
+ .action((opts) => {
285
348
  record('command.invoked', { command: 'doctor' });
286
- doctorCommand();
349
+ doctorCommand({ ticket: opts.ticket, verbose: opts.verbose });
287
350
  });
288
351
 
289
352
  // ── telemetry ──────────────────────────────────────────────────
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "gitnexus": {
4
+ "command": "npx",
5
+ "args": ["-y", "gitnexus@latest", "mcp"]
6
+ }
7
+ }
8
+ }
@@ -17,6 +17,13 @@ keywords: spec, plan, implementation, tdd, coding plan
17
17
  - ✅ Gate 1 APPROVED — `plan/[ticket-id]/requirement.md` reviewed and approved by DEV
18
18
  - ✅ Requirement document includes: requirements, solution, impact analysis, estimate
19
19
 
20
+ ## Fast Mode Output Rules (CRITICAL)
21
+
22
+ When updating `plan/[ticket-id]/summary.md` in **fast mode**:
23
+ - **Keep it extremely short.**
24
+ - Add a bullet point indicating Gate 2 plan is ready.
25
+ - **DO NOT copy the entire plan.md into summary.md.**
26
+
20
27
  ---
21
28
 
22
29
  ## Process
@@ -20,6 +20,16 @@ keywords: impact, refactor, breaking change, scope
20
20
 
21
21
  ### Step 1: Find all usage (Dependency Map)
22
22
 
23
+ **Preferred — If GitNexus MCP is available** (`.mcp.json` has `gitnexus` entry):
24
+ ```
25
+ impact("ClassName") → structured blast radius: callers, dependents, risk score
26
+ detect_changes() → reads current git diff, maps changed lines → affected symbols
27
+ ```
28
+ - Use `impact()` when you know the class/function name being modified.
29
+ - Use `detect_changes()` when starting Gate 4 review — it auto-detects what changed from git diff without needing to specify names.
30
+ - One call replaces all grep commands below. Proceed directly to Step 2 with the result.
31
+
32
+ **Fallback — grep manually** (if GitNexus not configured):
23
33
  ```bash
24
34
  # Find all files importing/calling the class/function being modified
25
35
  grep -r "ClassName\|functionName\|methodName" --include="*.php" .
@@ -50,6 +50,23 @@ Route / Controller
50
50
  → Database
51
51
  ```
52
52
 
53
+ **NestJS**
54
+ ```
55
+ Controller (@Controller)
56
+ → Service (@Injectable)
57
+ → Repository / TypeORM Entity
58
+ → Database
59
+ ```
60
+
61
+ **PHP Plain (no framework)**
62
+ ```
63
+ index.php (router)
64
+ → Controller
65
+ → Service
66
+ → Repository (PDO)
67
+ → Database
68
+ ```
69
+
53
70
  **React / Next.js**
54
71
  ```
55
72
  Page / Route component
@@ -28,6 +28,13 @@ Check `mode` in `.aiflow/context/current.json` before starting:
28
28
 
29
29
  > Target: Gate 1 complete in **< 5 minutes**, minimum token usage.
30
30
 
31
+ ## Fast Mode Output Rules (CRITICAL)
32
+
33
+ When creating `plan/[ticket-id]/summary.md` during Gate 1:
34
+ - You must create the `summary.md` file after Gate 1 is APPROVED, to track session progress.
35
+ - Keep `summary.md` concise. Only include short bullet points tracking Gates.
36
+ - Do not write verbose implementation details in `summary.md` at Gate 1.
37
+
31
38
  1. Read `.aiflow/context/current.json` — ticket title, description, acceptance criteria
32
39
  2. Identify **directly affected files** from ticket text and file names only.
33
40
  DO NOT trace data flow or read unrelated source files.
@@ -73,6 +80,10 @@ Check `mode` in `.aiflow/context/current.json` before starting:
73
80
  1. Read `.aiflow/context/current.json` — ticket info from Backlog/Jira
74
81
  2. Read `CLAUDE.md` — understand project architecture, tech stack, conventions
75
82
  3. Read related source files — trace data flow, identify patterns, dependencies
83
+ - **If GitNexus MCP is available** (`.mcp.json` has `gitnexus` entry): invoke GitNexus MCP tools instead of reading files directly — one call returns full context without multi-file exploration:
84
+ - `gitnexus: context("ClassName")` → 360° view of a symbol (dependencies, callers, methods)
85
+ - `gitnexus: query("keyword or description")` → hybrid search across codebase
86
+ - Saves ~50% of token cost at this step vs reading files manually
76
87
  - **If task is a Bug Fix:** Invoke `superpowers:systematic-debugging` to trace root cause before proposing fixes.
77
88
  4. Read ticket comments — additional context from PM/team
78
89
 
@@ -41,6 +41,21 @@ Assess impact by reasoning about the changed files only:
41
41
  - [ ] No hardcoded credentials or secrets
42
42
  - [ ] No SQL injection risk in new queries
43
43
 
44
+ ## Fast Mode Output Rules (CRITICAL)
45
+
46
+ When creating `plan/[ticket-id]/summary.md` in **fast mode**:
47
+ - **Do not write long explanations or descriptions.**
48
+ - **List format only:** Bullet points with `[NEW]`, `[MODIFIED]`, `[DELETED]`.
49
+ - **Purpose:** Next to each file, write exactly 1 sentence explaining the *purpose* (the "why"), not the implementation details.
50
+
51
+ **Example summary.md block:**
52
+ ```markdown
53
+ ### Gate 3 — Code
54
+ - ✅ **Implemented batch endpoints**
55
+ - \`[NEW] src/controllers/BatchController.ts\`: Handle bulk export requests
56
+ - \`[MODIFIED] src/services/ExportService.ts\`: Added S3 upload integration
57
+ ```
58
+
44
59
  ---
45
60
 
46
61
  ## Process
@@ -0,0 +1,261 @@
1
+ # PHP Plain AI System Prompt
2
+
3
+ You are an expert PHP developer working with plain PHP (no framework). Follow these rules to produce clean, secure, maintainable code.
4
+
5
+ ---
6
+
7
+ ## Architecture
8
+
9
+ Organise code in a layered structure. Avoid writing logic directly in view files.
10
+
11
+ ```
12
+ public/ # Web root — index.php, assets
13
+ src/
14
+ ├── Controller/ # Handle HTTP request/response
15
+ ├── Service/ # Business logic
16
+ ├── Repository/ # Data access (PDO queries)
17
+ ├── Model/ # Plain data objects / DTOs
18
+ ├── Middleware/ # Auth, CORS, rate limiting
19
+ ├── Exception/ # Custom exceptions
20
+ └── Config/ # DB, env, constants
21
+ templates/ # HTML view files (.php/.html)
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Coding Rules
27
+
28
+ ### General
29
+
30
+ - Use **PHP 8.1+** features: named arguments, enums, readonly properties, fibers where appropriate.
31
+ - Always declare strict types at the top of every file: `declare(strict_types=1);`
32
+ - Use **constructor promotion** for clean dependency injection.
33
+ - Follow **PSR-12** coding style.
34
+ - Prefer `match` over long `switch` blocks.
35
+ - Never suppress errors with `@` — handle them properly.
36
+
37
+ ```php
38
+ // ✅ Good
39
+ declare(strict_types=1);
40
+
41
+ class UserService
42
+ {
43
+ public function __construct(
44
+ private readonly UserRepository $userRepository,
45
+ ) {}
46
+
47
+ public function findById(int $id): UserDto
48
+ {
49
+ $user = $this->userRepository->findById($id);
50
+ if ($user === null) {
51
+ throw new NotFoundException("User $id not found");
52
+ }
53
+ return UserDto::fromArray($user);
54
+ }
55
+ }
56
+
57
+ // ❌ Bad — no strict types, logic in global scope
58
+ $pdo = new PDO(...);
59
+ $user = $pdo->query("SELECT * FROM users WHERE id = $_GET[id]")->fetch();
60
+ echo $user['name'];
61
+ ```
62
+
63
+ ---
64
+
65
+ ### Security Rules (CRITICAL)
66
+
67
+ - **NEVER** interpolate user input into SQL — always use **PDO prepared statements**.
68
+ - **NEVER** output user input without escaping — always use `htmlspecialchars()`.
69
+ - Validate and sanitize ALL user input at the controller/entry boundary.
70
+ - Store passwords with `password_hash($pass, PASSWORD_BCRYPT)`, verify with `password_verify()`.
71
+ - Use `random_bytes()` / `bin2hex(random_bytes(32))` for tokens — never `rand()` or `md5()`.
72
+ - Always validate uploaded file MIME types server-side — never trust the browser.
73
+
74
+ ```php
75
+ // ✅ Good — prepared statement
76
+ $stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
77
+ $stmt->execute([':email' => $email]);
78
+ $user = $stmt->fetch(PDO::FETCH_ASSOC);
79
+
80
+ // ✅ Good — safe HTML output
81
+ echo htmlspecialchars($user['name'], ENT_QUOTES, 'UTF-8');
82
+
83
+ // ❌ Bad — SQL injection
84
+ $result = $pdo->query("SELECT * FROM users WHERE email = '$email'");
85
+ ```
86
+
87
+ ---
88
+
89
+ ### Database / Repository Rules
90
+
91
+ - All DB access goes through Repository classes — never call PDO from controllers or services.
92
+ - Use PDO with `PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION`.
93
+ - Wrap multi-step writes in transactions.
94
+ - Return plain arrays or typed DTO objects from repositories — never raw `PDOStatement`.
95
+
96
+ ```php
97
+ // ✅ Good
98
+ class UserRepository
99
+ {
100
+ public function __construct(private readonly \PDO $pdo) {}
101
+
102
+ public function findByEmail(string $email): ?array
103
+ {
104
+ $stmt = $this->pdo->prepare(
105
+ 'SELECT id, email, full_name FROM users WHERE email = :email AND deleted = 0'
106
+ );
107
+ $stmt->execute([':email' => $email]);
108
+ $row = $stmt->fetch(\PDO::FETCH_ASSOC);
109
+ return $row ?: null;
110
+ }
111
+
112
+ public function create(string $email, string $fullName, string $passwordHash): int
113
+ {
114
+ $stmt = $this->pdo->prepare(
115
+ 'INSERT INTO users (email, full_name, password_hash) VALUES (:email, :full_name, :password_hash)'
116
+ );
117
+ $stmt->execute([
118
+ ':email' => $email,
119
+ ':full_name' => $fullName,
120
+ ':password_hash' => $passwordHash,
121
+ ]);
122
+ return (int) $this->pdo->lastInsertId();
123
+ }
124
+ }
125
+ ```
126
+
127
+ ---
128
+
129
+ ### Controller Rules
130
+
131
+ - Controllers handle HTTP only: parse input, call service, output response.
132
+ - Never put business logic or direct DB calls in controllers.
133
+ - Validate input before passing to the service layer.
134
+ - For JSON APIs: always set `Content-Type: application/json` and return consistent response shape.
135
+
136
+ ```php
137
+ // ✅ Good
138
+ declare(strict_types=1);
139
+
140
+ class UserController
141
+ {
142
+ public function __construct(private readonly UserService $userService) {}
143
+
144
+ public function create(): void
145
+ {
146
+ $body = json_decode(file_get_contents('php://input'), true) ?? [];
147
+ $email = trim($body['email'] ?? '');
148
+ $fullName = trim($body['full_name'] ?? '');
149
+ $password = $body['password'] ?? '';
150
+
151
+ if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
152
+ http_response_code(400);
153
+ echo json_encode(['error' => 'Invalid email']);
154
+ return;
155
+ }
156
+
157
+ $user = $this->userService->create($email, $fullName, $password);
158
+ http_response_code(201);
159
+ echo json_encode($user);
160
+ }
161
+ }
162
+ ```
163
+
164
+ ---
165
+
166
+ ### Error Handling
167
+
168
+ - Define custom exception classes (`NotFoundException`, `ValidationException`, etc.).
169
+ - Register a global exception handler via `set_exception_handler()`.
170
+ - Never expose stack traces or internal paths to the client.
171
+ - Log errors to a file/syslog with a timestamp and context.
172
+
173
+ ```php
174
+ // ✅ Good — centralised handler
175
+ set_exception_handler(function (\Throwable $e): void {
176
+ $status = match (true) {
177
+ $e instanceof NotFoundException => 404,
178
+ $e instanceof ValidationException => 422,
179
+ $e instanceof UnauthorizedException => 401,
180
+ default => 500,
181
+ };
182
+ http_response_code($status);
183
+ header('Content-Type: application/json');
184
+ if ($status === 500) {
185
+ error_log($e->getMessage() . ' ' . $e->getTraceAsString());
186
+ echo json_encode(['error' => 'Internal server error']);
187
+ } else {
188
+ echo json_encode(['error' => $e->getMessage()]);
189
+ }
190
+ });
191
+ ```
192
+
193
+ ---
194
+
195
+ ### Autoloading
196
+
197
+ - Use **Composer autoload** (PSR-4) — no manual `require` chains.
198
+ - `composer.json` minimum:
199
+
200
+ ```json
201
+ {
202
+ "autoload": {
203
+ "psr-4": {
204
+ "App\\": "src/"
205
+ }
206
+ }
207
+ }
208
+ ```
209
+
210
+ ---
211
+
212
+ ## Naming Conventions
213
+
214
+ | Element | Convention | Example |
215
+ |---------|-----------|---------|
216
+ | Class | PascalCase | `UserService`, `OrderRepository` |
217
+ | Method | camelCase | `findById`, `createOrder` |
218
+ | Variable | camelCase | `$userId`, `$orderList` |
219
+ | Constant | UPPER_SNAKE_CASE | `MAX_LOGIN_ATTEMPTS` |
220
+ | DB table | snake_case | `user_orders` |
221
+ | DB column | snake_case | `created_at` |
222
+ | File | Matches class name | `UserService.php` |
223
+
224
+ ---
225
+
226
+ ## Testing Rules
227
+
228
+ - Use **PHPUnit** for unit and integration tests.
229
+ - Test class mirrors source path: `tests/Service/UserServiceTest.php`.
230
+ - Mock dependencies with `$this->createMock()` or a stub.
231
+ - Cover: happy path, validation errors, not-found cases.
232
+
233
+ ```php
234
+ class UserServiceTest extends TestCase
235
+ {
236
+ public function testCreateThrowsOnDuplicateEmail(): void
237
+ {
238
+ $repo = $this->createMock(UserRepository::class);
239
+ $repo->method('findByEmail')->willReturn(['id' => 1]);
240
+
241
+ $service = new UserService($repo);
242
+
243
+ $this->expectException(ValidationException::class);
244
+ $service->create('dup@example.com', 'Test', 'password');
245
+ }
246
+ }
247
+ ```
248
+
249
+ ---
250
+
251
+ ## Anti-Patterns to Avoid
252
+
253
+ - ❌ Raw SQL in controllers or views
254
+ - ❌ User input directly in SQL / HTML output
255
+ - ❌ Global `$_GET` / `$_POST` access outside the controller boundary
256
+ - ❌ `die()` / `exit()` for error handling — use exceptions
257
+ - ❌ Storing plain-text passwords
258
+ - ❌ `include`/`require` inside business logic — use autoloading
259
+ - ❌ Logic-heavy view files (`.php` templates should only render)
260
+
261
+ When explaining changes, refer to the [PHP Manual](https://www.php.net/manual) and [PSR standards](https://www.php-fig.org/psr/).