@relipa/ai-flow-kit 0.0.7-beta.0 → 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.
- package/README.md +8 -3
- package/bin/aiflow.js +7 -0
- package/custom/skills/investigate-bug/SKILL.md +17 -0
- package/custom/templates/php-plain.md +261 -0
- package/docs/common/AIFLOW.md +6 -1
- package/docs/common/CHANGELOG.md +15 -1
- package/docs/common/QUICK_START.md +20 -2
- package/package.json +1 -1
- package/scripts/init.js +6 -1
- package/scripts/task.js +43 -4
- package/scripts/use.js +37 -6
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ 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
|
|
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
|
|
|
@@ -123,7 +123,7 @@ aiflow init --framework spring-boot,reactjs --adapter backlog,jira
|
|
|
123
123
|
- `.mcp.json` + Claude Desktop config — MCP adapter
|
|
124
124
|
- `.aiflow/credentials.json` — save API credentials
|
|
125
125
|
|
|
126
|
-
**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`
|
|
127
127
|
|
|
128
128
|
**Supported Adapters:** `backlog`, `jira`, `google-sheets`, `figma`
|
|
129
129
|
|
|
@@ -172,7 +172,7 @@ aiflow use PROJ-33 --comments-from 3
|
|
|
172
172
|
aiflow use --manual
|
|
173
173
|
|
|
174
174
|
# Load from local file (JSON or plain text)
|
|
175
|
-
aiflow use --file task.md
|
|
175
|
+
aiflow use --file task.md # Auto-generates taskId from filename & prompts for taskType
|
|
176
176
|
|
|
177
177
|
# Modes (Fast is default)
|
|
178
178
|
aiflow use PROJ-33 # Fast Mode: skip Q&A, target < 5 min
|
|
@@ -305,9 +305,14 @@ aiflow memory list
|
|
|
305
305
|
|-----------|---------|--------------|
|
|
306
306
|
| `spring-boot` | Java 17+ | `rules/java/` |
|
|
307
307
|
| `laravel` | PHP 8.1+ | `rules/php/` |
|
|
308
|
+
| `php-plain` | PHP 8.1+ (no framework) | `rules/php/` |
|
|
309
|
+
| `nestjs` | TypeScript | `rules/javascript/` |
|
|
308
310
|
| `reactjs` | TypeScript | `rules/javascript/` |
|
|
309
311
|
| `nextjs` | TypeScript | `rules/javascript/` |
|
|
310
312
|
| `vue-nuxt` | TypeScript | `rules/javascript/` |
|
|
313
|
+
| `nodejs-express` | JavaScript/TypeScript | `rules/javascript/` |
|
|
314
|
+
| `python-django` | Python | — |
|
|
315
|
+
| `python-fastapi` | Python | — |
|
|
311
316
|
|
|
312
317
|
---
|
|
313
318
|
|
package/bin/aiflow.js
CHANGED
|
@@ -278,6 +278,13 @@ program
|
|
|
278
278
|
command: `gate${gateNum}.${action}`,
|
|
279
279
|
ai_tool: options.aiTool || ''
|
|
280
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
|
+
}
|
|
281
288
|
});
|
|
282
289
|
|
|
283
290
|
// ── remove ────────────────────────────────────────────────────
|
|
@@ -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
|
|
@@ -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/).
|
package/docs/common/AIFLOW.md
CHANGED
|
@@ -65,7 +65,10 @@ AI Flow Kit allows you to manage multiple tasks in the same repository without l
|
|
|
65
65
|
- **`aiflow task pause`**: Save current context and gate progress to `.aiflow/tasks/<taskId>/`.
|
|
66
66
|
- **`aiflow task switch <id>`**: Pause current task and switch to another.
|
|
67
67
|
- **`aiflow task resume <id>`**: Restore context and gate state. AI auto-resumes from the correct gate.
|
|
68
|
-
- **`aiflow task next`**: Approve the current gate, clear context, and prepare the task for a fresh session (
|
|
68
|
+
- **`aiflow task next`**: Approve the current gate, generate `task-summary.md`, clear active context, and prepare the task for a fresh session (Recommended to avoid context pollution).
|
|
69
|
+
|
|
70
|
+
### Pro-Tip: Fresh Session Workflow
|
|
71
|
+
For the best AI performance, run `aiflow task next` after every gate approval, then open a **new chatbox** and type "continue". This clears out long chat history that might confuse the AI.
|
|
69
72
|
|
|
70
73
|
---
|
|
71
74
|
|
|
@@ -502,6 +505,8 @@ A: Yes. Use `aiflow task pause` to save your current progress, then `aiflow use
|
|
|
502
505
|
|
|
503
506
|
**Q: If I resume a task, will Claude know which gate to start at?**
|
|
504
507
|
A: Yes. The SessionStart hook reads `.aiflow/tasks/<taskId>/task-state.json` and injects gate-aware instructions. If Gate 1 was already approved, Claude will skip to Gate 2, and so on.
|
|
508
|
+
**Q: Can I resume a Gate 3 (Code Generation) task in a new chatbox?**
|
|
509
|
+
A: Yes. Gate 3 progress is saved via `[x]` checkboxes in `plan/[id]/plan.md`. If you open a new chatbox and say "continue", the AI will automatically skip completed tasks and pick up exactly where it left off.
|
|
505
510
|
|
|
506
511
|
**Q: How does AI estimate effort?**
|
|
507
512
|
A: AI analyzes the scope of changes (files affected, complexity, test requirements) and categorizes: S (< 1h), M (1-4h), L (4-8h), XL (8h+, should split).
|
package/docs/common/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,20 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
## [0.0.7] - 2026-05-
|
|
10
|
+
## [0.0.7] - 2026-05-08
|
|
11
11
|
|
|
12
12
|
### Added
|
|
13
13
|
|
|
14
|
+
- **`aiflow use --file` enhancements**:
|
|
15
|
+
- Automatic `taskId` generation from filename (up to 5 words).
|
|
16
|
+
- Full filename used as task `title`.
|
|
17
|
+
- Interactive `taskType` selection prompt during file loading for better context.
|
|
18
|
+
- **`documentation` task type** — Added support for documentation-specific tasks in `aiflow use` and task detection.
|
|
19
|
+
- **Automatic `task-summary.md` generation** — `aiflow task next` now generates a cumulative progress report in `plan/[ticket-id]/task-summary.md` at each gate finish.
|
|
20
|
+
- **Session Continuity Instructions** — Added explicit instructions on how to switch to a fresh chat session and resume tasks (including Gate 3 sub-tasks) to both CLI output and AI skill prompts.
|
|
21
|
+
- **NestJS framework support** — Added `nestjs` to the framework selector (`aiflow init`), language rule mapping (`javascript`), and AI instruction template (`custom/templates/nestjs.md`).
|
|
22
|
+
- **PHP Plain (no framework) support** — Added `php-plain` as a new framework option with a dedicated AI system prompt template covering strict types, PSR-12, PDO prepared statements, security best practices, Repository/Service/Controller layering, and PHPUnit testing.
|
|
23
|
+
- **`investigate-bug` skill: NestJS and PHP plain data flows** — Added framework-specific data flow traces for NestJS (`Controller → Service → Repository → DB`) and PHP Plain (`index.php → Controller → Service → Repository/PDO → DB`).
|
|
14
24
|
- **Intelligent AI Instruction Synchronization** — Marker-based (`<!-- aiflow-kit-start -->`) block updates for `CLAUDE.md`, `GEMINI.md`, and `.cursorrules`.
|
|
15
25
|
- **Interactive Instruction Safety** — Granular confirmation prompts for all instruction file modifications (update block, overwrite, or create new).
|
|
16
26
|
- **Automated Repository Hygiene** — Generated files and folders (`.aiflow/`, `plan/`, `.claude/`, `.rules/`, `.mcp.json`, and instruction files) are now automatically managed in `.gitignore`.
|
|
@@ -20,6 +30,8 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
20
30
|
|
|
21
31
|
### Changed
|
|
22
32
|
|
|
33
|
+
- **Robust Gate Detection** — Refactored `detectCurrentGate` to prioritize `task-state.json` as the source of truth for task progress.
|
|
34
|
+
- **`aiflow gate approved` CLI output** — Now prints a session-refresh tip to help users maintain clean AI contexts.
|
|
23
35
|
- **`aiflow update` efficiency** — Now automatically performs skill and instruction synchronization even if the version is unchanged (removes the need for `--force`).
|
|
24
36
|
- **Safety First Development** — Removed all automatic `git commit` instructions from AI skills (`subagent-driven-development`, `using-git-worktrees`, `writing-plans`) to ensure developer-led commit management.
|
|
25
37
|
- **Localized CLI** — All interactive prompts and confirmation messages translated to English for consistency.
|
|
@@ -29,6 +41,8 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
29
41
|
|
|
30
42
|
### Fixed
|
|
31
43
|
|
|
44
|
+
- **NestJS auto-detection** — `@nestjs/core` in `package.json` was incorrectly detected as `nodejs-express`; now correctly resolves to `nestjs`.
|
|
45
|
+
- **PHP project auto-detection** — Projects with `composer.json` but no Laravel dependency now correctly auto-detect as `php-plain` instead of being skipped.
|
|
32
46
|
- Fixed `.github/copilot-instructions.md` not being included in automated `.gitignore` rules.
|
|
33
47
|
|
|
34
48
|
---
|
|
@@ -216,9 +216,9 @@ Ask a teammate to review. They can use `superpowers:receiving-code-review` skill
|
|
|
216
216
|
|
|
217
217
|
After peer reviewer approves → merge → task complete.
|
|
218
218
|
|
|
219
|
-
### Step 9: Task Management
|
|
219
|
+
### Step 9: Task Management & Session Continuity
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
Manage multiple tasks and ensure clean transitions between gates to prevent "context pollution" in long AI sessions.
|
|
222
222
|
|
|
223
223
|
```bash
|
|
224
224
|
# Pause current task (saves gate progress)
|
|
@@ -229,8 +229,26 @@ aiflow task list
|
|
|
229
229
|
|
|
230
230
|
# Resume a previously paused task
|
|
231
231
|
aiflow task resume PROJ-33
|
|
232
|
+
|
|
233
|
+
# Approve gate and prepare for fresh session (Recommended)
|
|
234
|
+
aiflow task next
|
|
232
235
|
```
|
|
233
236
|
|
|
237
|
+
#### Best Practice: Refreshing Sessions
|
|
238
|
+
To keep the AI focused and avoid context pollution (where the AI gets confused by long chat history), it is recommended to start a **fresh chat session** after completing each gate.
|
|
239
|
+
|
|
240
|
+
1. Run `aiflow task next` in your terminal to approve the current gate.
|
|
241
|
+
2. Open a **NEW chatbox** (Claude UI, Cursor, etc.).
|
|
242
|
+
3. Run `aiflow task resume [ticket-id]` (if not already active).
|
|
243
|
+
4. Type **"continue"** — the AI will read the latest state and start the next gate with a clean slate.
|
|
244
|
+
|
|
245
|
+
#### Resuming Gate 3 (Code Generation)
|
|
246
|
+
If you need to switch chatboxes in the middle of a complex coding task (Gate 3):
|
|
247
|
+
- Gate 3 progress is saved via `[x]` checkboxes in the plan file (`plan/[id]/plan.md`).
|
|
248
|
+
- Simply open a new chatbox, run `aiflow task resume`, and tell the AI to **"continue executing the plan"**.
|
|
249
|
+
- The AI will automatically skip finished tasks and pick up exactly where you left off.
|
|
250
|
+
|
|
251
|
+
|
|
234
252
|
---
|
|
235
253
|
|
|
236
254
|
## Workflow Summary Diagram
|
package/package.json
CHANGED
package/scripts/init.js
CHANGED
|
@@ -24,10 +24,12 @@ function commandExists(cmd) {
|
|
|
24
24
|
// Map framework → language family for picking the right rules
|
|
25
25
|
const FRAMEWORK_LANGUAGE = {
|
|
26
26
|
'laravel': 'php',
|
|
27
|
+
'php-plain': 'php',
|
|
27
28
|
'spring-boot': 'java',
|
|
28
29
|
'reactjs': 'javascript',
|
|
29
30
|
'nextjs': 'javascript',
|
|
30
31
|
'vue-nuxt': 'javascript',
|
|
32
|
+
'nestjs': 'javascript',
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
const AI_TOOL_FILES = {
|
|
@@ -65,7 +67,7 @@ async function detectFramework(projectDir) {
|
|
|
65
67
|
if (deps['next']) return 'nextjs';
|
|
66
68
|
if (deps['nuxt']) return 'vue-nuxt';
|
|
67
69
|
if (deps['vue']) return 'vue-nuxt';
|
|
68
|
-
if (deps['@nestjs/core']) return '
|
|
70
|
+
if (deps['@nestjs/core']) return 'nestjs';
|
|
69
71
|
if (deps['react']) return 'reactjs';
|
|
70
72
|
if (deps['express'] || deps['fastify']) return 'nodejs-express';
|
|
71
73
|
}
|
|
@@ -78,6 +80,7 @@ async function detectFramework(projectDir) {
|
|
|
78
80
|
if (await fs.pathExists(composerPath)) {
|
|
79
81
|
const composer = await fs.readJson(composerPath).catch(() => ({}));
|
|
80
82
|
if ((composer.require || {})['laravel/framework']) return 'laravel';
|
|
83
|
+
return 'php-plain';
|
|
81
84
|
}
|
|
82
85
|
// Python
|
|
83
86
|
if (await fs.pathExists(path.join(projectDir, 'manage.py'))) return 'python-django';
|
|
@@ -96,6 +99,8 @@ const FRAMEWORK_CHOICES = [
|
|
|
96
99
|
{ name: 'Next.js', value: 'nextjs' },
|
|
97
100
|
{ name: 'Vue / Nuxt', value: 'vue-nuxt' },
|
|
98
101
|
{ name: 'Laravel (PHP)', value: 'laravel' },
|
|
102
|
+
{ name: 'PHP (no framework)', value: 'php-plain' },
|
|
103
|
+
{ name: 'NestJS', value: 'nestjs' },
|
|
99
104
|
{ name: 'Node.js / Express', value: 'nodejs-express' },
|
|
100
105
|
{ name: 'Python Django', value: 'python-django' },
|
|
101
106
|
{ name: 'Python FastAPI', value: 'python-fastapi' },
|
package/scripts/task.js
CHANGED
|
@@ -345,13 +345,23 @@ async function nextGate(taskId) {
|
|
|
345
345
|
// Clear current context so no task is "active"
|
|
346
346
|
await fs.remove(CURRENT_FILE);
|
|
347
347
|
|
|
348
|
+
// Generate cumulative task-summary.md
|
|
349
|
+
const planDir = path.join(PROJECT_DIR, 'plan', resolvedId);
|
|
350
|
+
await fs.ensureDir(planDir);
|
|
351
|
+
const summaryPath = path.join(planDir, 'task-summary.md');
|
|
352
|
+
const summaryContent = await generateMarkdownSummary(taskState);
|
|
353
|
+
await fs.writeFile(summaryPath, summaryContent, 'utf-8');
|
|
354
|
+
|
|
348
355
|
const gateLabels = { 1: 'Analyze', 2: 'Plan', 3: 'Code', 4: 'Review', 5: 'PR' };
|
|
349
356
|
console.log(chalk.green(`✓ Gate ${currentGate} approved for ${resolvedId}.`));
|
|
357
|
+
console.log(chalk.gray(` Summary saved to: plan/${resolvedId}/task-summary.md`));
|
|
350
358
|
console.log(chalk.white(`\n Next: Gate ${nextGateNum} — ${gateLabels[nextGateNum] || 'Done'}`));
|
|
351
|
-
console.log(chalk.cyan(
|
|
352
|
-
console.log(chalk.gray(
|
|
353
|
-
console.log(chalk.gray(` 2. Run: aiflow task resume ${resolvedId}
|
|
354
|
-
console.log(chalk.gray(` 3.
|
|
359
|
+
console.log(chalk.cyan(`\n To continue in a fresh session (Recommended to avoid context pollution):`));
|
|
360
|
+
console.log(chalk.gray(` 1. Open a NEW chatbox or terminal session.`));
|
|
361
|
+
console.log(chalk.gray(` 2. Run: aiflow task resume ${resolvedId} (to load context).`));
|
|
362
|
+
console.log(chalk.gray(` 3. Type "start" or "continue from the current plan".`));
|
|
363
|
+
console.log(chalk.yellow(` (Note: Gate 3 progress is saved via [x] checkboxes in plan.md.`));
|
|
364
|
+
console.log(chalk.yellow(` The AI will automatically resume the exact task you left off.)`));
|
|
355
365
|
console.log();
|
|
356
366
|
}
|
|
357
367
|
|
|
@@ -375,6 +385,12 @@ async function loadAllTaskStates() {
|
|
|
375
385
|
}
|
|
376
386
|
|
|
377
387
|
async function detectCurrentGate(taskId) {
|
|
388
|
+
const statePath = path.join(TASKS_DIR, taskId, 'task-state.json');
|
|
389
|
+
if (await fs.pathExists(statePath)) {
|
|
390
|
+
const state = await fs.readJson(statePath).catch(() => ({}));
|
|
391
|
+
if (state.currentGate) return state.currentGate;
|
|
392
|
+
}
|
|
393
|
+
|
|
378
394
|
const planDir = path.join(PROJECT_DIR, 'plan', taskId);
|
|
379
395
|
if (!(await fs.pathExists(planDir))) return 1;
|
|
380
396
|
if (await fs.pathExists(path.join(planDir, 'summary.md'))) return 5;
|
|
@@ -383,6 +399,29 @@ async function detectCurrentGate(taskId) {
|
|
|
383
399
|
return 1;
|
|
384
400
|
}
|
|
385
401
|
|
|
402
|
+
async function generateMarkdownSummary(taskState) {
|
|
403
|
+
const lines = [];
|
|
404
|
+
lines.push(`# Task Summary: ${taskState.taskId}`);
|
|
405
|
+
lines.push(`**Title:** ${taskState.title}`);
|
|
406
|
+
lines.push(`**Status:** ${taskState.status}`);
|
|
407
|
+
lines.push(`**Current Gate:** ${taskState.currentGate} (${gateLabel(taskState.currentGate)})`);
|
|
408
|
+
lines.push(`**Updated At:** ${new Date().toLocaleString()}`);
|
|
409
|
+
lines.push(``);
|
|
410
|
+
lines.push(`## Gate History`);
|
|
411
|
+
lines.push(`| Gate | Status | Approved At |`);
|
|
412
|
+
lines.push(`|------|--------|-------------|`);
|
|
413
|
+
for (let i = 1; i <= 5; i++) {
|
|
414
|
+
const approvedAt = taskState.gateApprovals && taskState.gateApprovals[String(i)];
|
|
415
|
+
const status = approvedAt ? '✅ Approved' : (i === taskState.currentGate ? '⏳ In Progress' : '⚪ Pending');
|
|
416
|
+
const timeStr = approvedAt ? new Date(approvedAt).toLocaleString() : '-';
|
|
417
|
+
lines.push(`| Gate ${i} (${gateLabel(i)}) | ${status} | ${timeStr} |`);
|
|
418
|
+
}
|
|
419
|
+
lines.push(``);
|
|
420
|
+
lines.push(`---`);
|
|
421
|
+
lines.push(`*Auto-generated by aiflow task next*`);
|
|
422
|
+
return lines.join('\n');
|
|
423
|
+
}
|
|
424
|
+
|
|
386
425
|
function gateLabel(n) {
|
|
387
426
|
const labels = {
|
|
388
427
|
1: 'AI Analyze Requirement',
|
package/scripts/use.js
CHANGED
|
@@ -459,7 +459,8 @@ async function manualContext(prefillId = '') {
|
|
|
459
459
|
{ name: '✨ Feature', value: 'feature' },
|
|
460
460
|
{ name: '🔍 Investigation', value: 'investigation' },
|
|
461
461
|
{ name: '♻️ Refactor', value: 'refactor' },
|
|
462
|
-
{ name: '📊 Impact Analysis', value: 'impact-analysis' }
|
|
462
|
+
{ name: '📊 Impact Analysis', value: 'impact-analysis' },
|
|
463
|
+
{ name: '📖 Documentation', value: 'documentation' }
|
|
463
464
|
],
|
|
464
465
|
default: existing.taskType || undefined
|
|
465
466
|
});
|
|
@@ -504,10 +505,25 @@ async function loadFromFile(filePath, options = {}) {
|
|
|
504
505
|
context = JSON.parse(raw);
|
|
505
506
|
} catch (_) {
|
|
506
507
|
console.log(chalk.gray(' File is not JSON — auto-detecting task info from content...'));
|
|
507
|
-
context = buildContextFromPlainText(raw);
|
|
508
|
+
context = buildContextFromPlainText(raw, filePath);
|
|
508
509
|
}
|
|
509
510
|
|
|
510
511
|
context.mode = options.full ? 'full' : 'fast';
|
|
512
|
+
|
|
513
|
+
// Prompt for task type
|
|
514
|
+
context.taskType = await select({
|
|
515
|
+
message: 'Task type:',
|
|
516
|
+
choices: [
|
|
517
|
+
{ name: '🐛 Bug Fix', value: 'bug-fix' },
|
|
518
|
+
{ name: '✨ Feature', value: 'feature' },
|
|
519
|
+
{ name: '🔍 Investigation', value: 'investigation' },
|
|
520
|
+
{ name: '♻️ Refactor', value: 'refactor' },
|
|
521
|
+
{ name: '📊 Impact Analysis', value: 'impact-analysis' },
|
|
522
|
+
{ name: '📖 Documentation', value: 'documentation' }
|
|
523
|
+
],
|
|
524
|
+
default: context.taskType || 'feature'
|
|
525
|
+
});
|
|
526
|
+
|
|
511
527
|
await saveContext(context, options.save);
|
|
512
528
|
printContextSummary(context);
|
|
513
529
|
await suggestNextStep();
|
|
@@ -517,14 +533,29 @@ async function loadFromFile(filePath, options = {}) {
|
|
|
517
533
|
* Build a minimal context object from arbitrary plain-text file content.
|
|
518
534
|
* Tries to detect ticket ID (e.g. PROJ-33, APP-123) and a title from the first non-blank line.
|
|
519
535
|
*/
|
|
520
|
-
function buildContextFromPlainText(text) {
|
|
536
|
+
function buildContextFromPlainText(text, filePath = '') {
|
|
521
537
|
// Detect ticket ID: first PROJ-123 style token anywhere in the text
|
|
522
538
|
const idMatch = text.match(/\b([A-Z][A-Z0-9_]+-\d+)\b/);
|
|
523
|
-
const taskId = idMatch ? idMatch[1] : `TASK-${Date.now()}`;
|
|
524
539
|
|
|
525
|
-
|
|
540
|
+
let taskId;
|
|
541
|
+
if (idMatch) {
|
|
542
|
+
taskId = idMatch[1];
|
|
543
|
+
} else {
|
|
544
|
+
const fileNameOnly = filePath ? path.basename(filePath, path.extname(filePath)) : '';
|
|
545
|
+
const cleanWords = fileNameOnly.replace(/[^a-zA-Z0-9]/g, ' ')
|
|
546
|
+
.split(/\s+/)
|
|
547
|
+
.filter(Boolean)
|
|
548
|
+
.slice(0, 5)
|
|
549
|
+
.join('-')
|
|
550
|
+
.toUpperCase();
|
|
551
|
+
const prefix = cleanWords ? `TASK-${cleanWords}-` : 'TASK-';
|
|
552
|
+
taskId = `${prefix}${Date.now()}`;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Title: use full filename if available, otherwise first non-blank line
|
|
556
|
+
const fullFileName = filePath ? path.basename(filePath) : '';
|
|
526
557
|
const lines = text.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
|
|
527
|
-
const titleLine = lines.find(l => l.length > 3) || taskId;
|
|
558
|
+
const titleLine = fullFileName || lines.find(l => l.length > 3) || taskId;
|
|
528
559
|
const title = titleLine.substring(0, 120);
|
|
529
560
|
|
|
530
561
|
// Task type detection from text
|