@relipa/ai-flow-kit 0.1.2-beta.0 → 0.1.3
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 +26 -0
- package/bin/aiflow.js +40 -1
- package/custom/harness/playwright/.env.example +10 -0
- package/custom/harness/playwright/playwright.config.ts +34 -0
- package/custom/harness/playwright/tests/e2e/auth.setup.ts +25 -0
- package/custom/harness/playwright/tests/e2e/fixtures/test.ts +6 -0
- package/custom/harness/playwright/tests/e2e/pages/BasePage.ts +9 -0
- package/custom/harness/playwright/tests/e2e/support/auth.ts +5 -0
- package/custom/mcp-presets/playwright.json +8 -0
- package/custom/rules/test-patterns.md +70 -0
- package/custom/skills/automation-testing/SKILL.md +239 -0
- package/custom/skills/automation-testing/templates/BasePage.ts +29 -0
- package/custom/skills/automation-testing/templates/PageObject.example.ts +29 -0
- package/custom/skills/automation-testing/templates/playwright.config.ts +39 -0
- package/custom/skills/automation-testing/templates/spec.example.ts +29 -0
- package/custom/skills/coverage-check/SKILL.md +202 -0
- package/custom/skills/evidence-aggregation/SKILL.md +246 -0
- package/custom/skills/execute-flow/SKILL.md +479 -0
- package/custom/skills/execute-flow/templates/playwright.config.ts +39 -0
- package/custom/skills/generate-test-report/SKILL.md +99 -0
- package/custom/skills/generate-test-report/templates/test-report.md +58 -0
- package/custom/skills/generate-testcase/SKILL.md +303 -0
- package/custom/skills/generate-testcase/templates/testcase.md +88 -0
- package/custom/skills/log-bug/SKILL.md +131 -0
- package/custom/skills/pr-impact-analysis/SKILL.md +180 -0
- package/custom/skills/retest-orchestration/SKILL.md +191 -0
- package/custom/skills/script-sync/SKILL.md +208 -0
- package/custom/skills/test-analysis/SKILL.md +262 -0
- package/custom/templates/shared/gate-workflow.md +306 -2
- package/docs/common/CHANGELOG.md +84 -0
- package/docs/common/QUICK_START.md +30 -0
- package/docs/common/cli-reference.md +48 -7
- package/package.json +1 -1
- package/scripts/create-score-excel.js +20 -7
- package/scripts/detect.js +10 -0
- package/scripts/guide.js +9 -0
- package/scripts/prompt.js +36 -0
- package/scripts/scaffold-playwright.js +106 -0
- package/scripts/task.js +21 -7
- package/scripts/update.js +124 -124
- package/scripts/use.js +30 -12
|
@@ -358,6 +358,31 @@ aiflow use PROJ-88
|
|
|
358
358
|
claude # AI auto-starts: map dependencies → assess risk → output report
|
|
359
359
|
```
|
|
360
360
|
|
|
361
|
+
### Execute Tests (QA Tester)
|
|
362
|
+
|
|
363
|
+
Run existing test cases with Playwright — sync scripts, execute, collect evidence, and generate report.
|
|
364
|
+
|
|
365
|
+
**Entry point 1: from a ticket** (ticket `type` must be `"execute"`)
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
aiflow use PROJ-44 # loads ticket with taskType "execute"
|
|
369
|
+
claude # AI auto-starts execute-flow Gate 1 (pre-flight check)
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**Entry point 2: from a TC file**
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
ak execute ./testcases/AD10.md # AI starts execute-flow with this TC file
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**Entry point 3: manual**
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
ak execute # AI asks: "TC file ở đâu?"
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Output lands in `ak-test/{repo}/results/{screenId}/run-{N}/` — never deleted between runs.
|
|
385
|
+
|
|
361
386
|
### Figma Design → Code
|
|
362
387
|
|
|
363
388
|
Turn a Figma design into a React / Next.js / Vue / Angular component automatically.
|
|
@@ -414,6 +439,11 @@ ak g -f # architecture diagram (ak guide
|
|
|
414
439
|
ak g -c # command reference (ak guide --commands)
|
|
415
440
|
ak sync # sync aiflow instruction files (ak sync-skills)
|
|
416
441
|
|
|
442
|
+
# Execute flow (QA Tester)
|
|
443
|
+
ak execute PROJ-44 # execute từ ticket (taskType phải là "execute")
|
|
444
|
+
ak execute ./testcases/AD10.md # execute từ TC file
|
|
445
|
+
ak execute # execute manual (AI hỏi TC file)
|
|
446
|
+
|
|
417
447
|
# Per task
|
|
418
448
|
ak use PROJ-33 # load context (Fast Mode, default) — alias: ak u PROJ-33
|
|
419
449
|
ak use PROJ-33 PROJ-10 docs/arch.md # multi-target: primary + supplementary (v0.1.0+)
|
|
@@ -17,13 +17,13 @@ ak <alias> --help # Also works with aliases
|
|
|
17
17
|
| Full command | Short | Full command | Short |
|
|
18
18
|
|---|---|---|---|
|
|
19
19
|
| `ak init` | `ak i` | `ak use` | `ak u` |
|
|
20
|
-
| `ak
|
|
21
|
-
| `ak
|
|
22
|
-
| `ak
|
|
23
|
-
| `ak
|
|
24
|
-
| `ak
|
|
25
|
-
| `ak
|
|
26
|
-
| `ak
|
|
20
|
+
| `ak execute` | `ak ex` | `ak prompt` | `ak p` |
|
|
21
|
+
| `ak detect` | `ak d` | `ak task` | `ak t` |
|
|
22
|
+
| `ak context` | `ak ctx` | `ak checkpoint` | `ak cp` |
|
|
23
|
+
| `ak validate` | `ak vl` | `ak memory` | `ak mem` |
|
|
24
|
+
| `ak guide` | `ak g` | `ak remove` | `ak rm` |
|
|
25
|
+
| `ak update` | `ak up` | `ak sync-skills` | `ak sync` |
|
|
26
|
+
| `ak doctor` | `ak dr` | `ak telemetry` | `ak tel` |
|
|
27
27
|
|
|
28
28
|
| `task` sub-command | Short | `memory` sub-command | Short |
|
|
29
29
|
|---|---|---|---|
|
|
@@ -156,6 +156,47 @@ $ aiflow use PROJ-33 PROJ-10 docs/arch.md
|
|
|
156
156
|
✓ Auto-resolved 2 link(s) from description
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
+
## execute (alias: `ex`)
|
|
160
|
+
|
|
161
|
+
Execute test cases via the 4-gate Executing Flow. Syncs Playwright scripts, runs them, collects evidence, and generates a test report.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
ak execute PROJ-44 # from ticket (taskType must be "execute")
|
|
165
|
+
ak execute ./testcases/AD10.md # from TC file
|
|
166
|
+
ak execute # manual — AI asks for TC file
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Arguments:**
|
|
170
|
+
- `[target]` — Optional. A ticket ID (`PROJ-44`), a path to a TC file (`./testcases/AD10.md`), or omitted for manual entry.
|
|
171
|
+
|
|
172
|
+
**What it does (4 gates):**
|
|
173
|
+
1. **Gate 1 — Pre-flight:** Parse TC file, verify `ak-test/{repo}/` and `playwright.config.ts` exist, check `BASE_URL`
|
|
174
|
+
2. **Gate 2 — Script Sync:** Hash-based sync — gen new TCs, update changed, skip unchanged. Uses Playwright MCP for real selectors (never fabricates).
|
|
175
|
+
3. **Gate 3 — Execute & Evidence:** Run Playwright, organize evidence into `run-{N}/`, update TC file R1/R2 columns (`✅ Pass` · `❌ Fail` · `⏭️ Untest` · `⏳ Pending`)
|
|
176
|
+
4. **Gate 4 — Report & Bug Logging:** Generate `testreport.md`, confirm each bug with TESTER before logging to Jira
|
|
177
|
+
|
|
178
|
+
**Output structure:**
|
|
179
|
+
```
|
|
180
|
+
ak-test/{repo}/
|
|
181
|
+
├── scripts/{screenId}/{ScreenID}.spec.ts
|
|
182
|
+
└── results/{screenId}/run-{N}/
|
|
183
|
+
├── {TC_ID}-{scenario}/ (screenshots, trace.zip, result.md)
|
|
184
|
+
├── bugs/BUG-NNN-{slug}.md
|
|
185
|
+
└── testreport.md
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Example:**
|
|
189
|
+
```bash
|
|
190
|
+
$ ak execute ./testcases/AD10.md
|
|
191
|
+
⏸️ GATE 1: EXECUTE FLOW READY
|
|
192
|
+
TC file: testcases/AD10.md
|
|
193
|
+
Repo: repo-fe | Screen: AD10 — Create Product
|
|
194
|
+
BASE_URL: http://localhost:3000
|
|
195
|
+
→ Type APPROVED to sync scripts (Gate 2)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
159
200
|
## fetch-links
|
|
160
201
|
|
|
161
202
|
Fetch a single Backlog/Jira URL and print a `SupplementaryContext` JSON object to stdout. Intended for AI runtime use inside the `read-study-requirement` skill — when AI encounters a link in the ticket description that wasn't already resolved by `ak use`, it can call this command and integrate the JSON output.
|
package/package.json
CHANGED
|
@@ -193,9 +193,9 @@ function buildRow(email, u) {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
// ── Excel writer ───────────────────────────────────────────────────────────
|
|
196
|
-
async function writeExcel(rows, outputPath) {
|
|
196
|
+
async function writeExcel(rows, outputPath, sheetLabel, fromLabel, toLabel) {
|
|
197
197
|
const workbook = new ExcelJS.Workbook();
|
|
198
|
-
const sheet = workbook.addWorksheet(
|
|
198
|
+
const sheet = workbook.addWorksheet(`Score ${sheetLabel}`);
|
|
199
199
|
|
|
200
200
|
sheet.columns = [
|
|
201
201
|
{ key: 'no', width: 5 },
|
|
@@ -220,7 +220,7 @@ async function writeExcel(rows, outputPath) {
|
|
|
220
220
|
};
|
|
221
221
|
|
|
222
222
|
// Title row
|
|
223
|
-
sheet.addRow([
|
|
223
|
+
sheet.addRow([`AI Flow Kit — Weekly Score Report: ${fromLabel} – ${toLabel}`]);
|
|
224
224
|
sheet.mergeCells('A1:N1');
|
|
225
225
|
const title = sheet.getCell('A1');
|
|
226
226
|
title.font = { bold: true, size: 13, color: { argb: 'FF1F3864' } };
|
|
@@ -296,7 +296,7 @@ const CANONICAL_USERS = [
|
|
|
296
296
|
'nguyenlt@relipasoft.com',
|
|
297
297
|
'thuongvv@relipasoft.com',
|
|
298
298
|
'yenvtb@relipasoft.com',
|
|
299
|
-
'
|
|
299
|
+
'tructh@relipasoft.com',
|
|
300
300
|
'thainq@relipasoft.com',
|
|
301
301
|
'tuoittx@relipasoft.com',
|
|
302
302
|
'hungdv@relipasoft.com',
|
|
@@ -323,8 +323,21 @@ const EMPTY_USER = (email) => ({
|
|
|
323
323
|
|
|
324
324
|
// ── Main ───────────────────────────────────────────────────────────────────
|
|
325
325
|
(async () => {
|
|
326
|
-
|
|
327
|
-
|
|
326
|
+
// Usage: node create-score-excel.js [log-file-stem]
|
|
327
|
+
// e.g.: node create-score-excel.js log_08-06-2026_14-06-2026
|
|
328
|
+
const stem = process.argv[2] || 'log_18-05-2026_24-05-2026';
|
|
329
|
+
|
|
330
|
+
// Derive display dates from stem: log_DD-MM-YYYY_DD-MM-YYYY → DD/MM/YYYY – DD/MM/YYYY
|
|
331
|
+
const dateMatch = stem.match(/log_(\d{2}-\d{2}-\d{4})_(\d{2}-\d{2}-\d{4})/);
|
|
332
|
+
const [fromLabel, toLabel] = dateMatch
|
|
333
|
+
? [dateMatch[1].replace(/-/g, '/'), dateMatch[2].replace(/-/g, '/')]
|
|
334
|
+
: ['?', '?'];
|
|
335
|
+
const sheetLabel = dateMatch
|
|
336
|
+
? `${dateMatch[1].slice(0,5).replace(/-/g,'.')}-${dateMatch[2].slice(0,5).replace(/-/g,'.')}.${dateMatch[1].slice(6,10)}`
|
|
337
|
+
: stem;
|
|
338
|
+
|
|
339
|
+
const logFile = path.join(__dirname, '..', 'tellog', stem);
|
|
340
|
+
const outputFile = path.join(__dirname, '..', 'tellog', `${stem}.xlsx`);
|
|
328
341
|
|
|
329
342
|
const rawUsers = parseLog(logFile);
|
|
330
343
|
|
|
@@ -350,5 +363,5 @@ const EMPTY_USER = (email) => ({
|
|
|
350
363
|
return buildRow(email, u);
|
|
351
364
|
});
|
|
352
365
|
|
|
353
|
-
await writeExcel(rows, outputFile);
|
|
366
|
+
await writeExcel(rows, outputFile, sheetLabel, fromLabel, toLabel);
|
|
354
367
|
})();
|
package/scripts/detect.js
CHANGED
|
@@ -64,6 +64,16 @@ class TaskDetector {
|
|
|
64
64
|
antiKeywords: ['code'],
|
|
65
65
|
icon: '📖',
|
|
66
66
|
description: 'Writing documentation'
|
|
67
|
+
},
|
|
68
|
+
'testing': {
|
|
69
|
+
keywords: [
|
|
70
|
+
'test', 'testing', 'unit test', 'integration test', 'e2e',
|
|
71
|
+
'qa', 'quality assurance', 'coverage', 'spec', 'assert',
|
|
72
|
+
'mock', 'stub', 'test case', 'test suite', 'automated test'
|
|
73
|
+
],
|
|
74
|
+
antiKeywords: [],
|
|
75
|
+
icon: '🧪',
|
|
76
|
+
description: 'Writing tests and QA'
|
|
67
77
|
}
|
|
68
78
|
};
|
|
69
79
|
}
|
package/scripts/guide.js
CHANGED
|
@@ -181,6 +181,14 @@ function showCommands() {
|
|
|
181
181
|
['aiflow detect "<description>"', '', 'Auto-detect task type'],
|
|
182
182
|
]
|
|
183
183
|
},
|
|
184
|
+
{
|
|
185
|
+
group: 'QA Execute Flow',
|
|
186
|
+
items: [
|
|
187
|
+
['aiflow execute <TICKET-ID>', '', 'Execute tests from ticket (taskType: execute)'],
|
|
188
|
+
['aiflow execute <TC-FILE>', '', 'Execute tests from TC file path'],
|
|
189
|
+
['aiflow execute', '', 'Execute tests — manual (AI asks for TC file)'],
|
|
190
|
+
]
|
|
191
|
+
},
|
|
184
192
|
{
|
|
185
193
|
group: 'Task management',
|
|
186
194
|
items: [
|
|
@@ -279,6 +287,7 @@ function showPromptGuide() {
|
|
|
279
287
|
['refactor', 'Improving code without changing behavior'],
|
|
280
288
|
['impact-analysis', 'Analyzing the impact of changes'],
|
|
281
289
|
['documentation', 'Writing docs, README, API reference'],
|
|
290
|
+
['testing', 'Writing tests, test coverage, QA'],
|
|
282
291
|
];
|
|
283
292
|
|
|
284
293
|
for (const [type, when] of types) {
|
package/scripts/prompt.js
CHANGED
|
@@ -215,6 +215,42 @@ AI must:
|
|
|
215
215
|
5. Display "GATE 1: Impact report complete" → wait for **APPROVED**
|
|
216
216
|
|
|
217
217
|
**Do NOT make any code changes.** Implementation needs a separate ticket.
|
|
218
|
+
`,
|
|
219
|
+
},
|
|
220
|
+
'testing': {
|
|
221
|
+
header: 'Testing',
|
|
222
|
+
instruction: 'Write tests for the feature or scenario described below.',
|
|
223
|
+
skillWorkflow: `
|
|
224
|
+
## STRICT GATE WORKFLOW — Testing
|
|
225
|
+
|
|
226
|
+
### GATE 1 — AI Analyze Requirement
|
|
227
|
+
**INVOKE:** \`read-study-requirement\` skill
|
|
228
|
+
AI must:
|
|
229
|
+
1. Load ticket context + read source code thoroughly
|
|
230
|
+
2. Understand what needs to be tested (unit, integration, e2e)
|
|
231
|
+
3. If unclear → ask ONE question at a time
|
|
232
|
+
4. Output \`plan/[ticket-id]/requirement.md\` with:
|
|
233
|
+
- Test scope and strategy
|
|
234
|
+
- Test cases to cover (happy path + edge cases + failure cases)
|
|
235
|
+
- Source code references
|
|
236
|
+
- Effort estimate
|
|
237
|
+
5. Display "GATE 1: Test plan ready" → wait for **APPROVED**
|
|
238
|
+
|
|
239
|
+
### GATE 2 — Implementation Plan
|
|
240
|
+
**INVOKE:** \`generate-spec\` skill, then \`superpowers:writing-plans\`
|
|
241
|
+
- Create detailed TDD test implementation plan.
|
|
242
|
+
- Output \`plan/[ticket-id]/plan.md\`.
|
|
243
|
+
- Display: "GATE 2 PAUSED: type APPROVED to start coding".
|
|
244
|
+
|
|
245
|
+
### GATE 3 — Write Tests (TDD)
|
|
246
|
+
Only runs after Gate 2 APPROVED.
|
|
247
|
+
**INVOKE:** \`superpowers:test-driven-development\`
|
|
248
|
+
- Write failing tests first → confirm FAIL → implement → confirm PASS.
|
|
249
|
+
|
|
250
|
+
### GATE 4 — AI Self-Review (wait for APPROVED)
|
|
251
|
+
**INVOKE:** \`superpowers:verification-before-completion\`
|
|
252
|
+
All tests must PASS. Check coverage meets requirements.
|
|
253
|
+
Display: "GATE 4 PAUSED: type APPROVED or BUG: [description]"
|
|
218
254
|
`,
|
|
219
255
|
},
|
|
220
256
|
'documentation': {
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
|
|
7
|
+
const PKG_DIR = path.join(__dirname, '..');
|
|
8
|
+
const HARNESS_SRC = path.join(PKG_DIR, 'custom', 'harness', 'playwright');
|
|
9
|
+
|
|
10
|
+
const HARNESS_FILES = [
|
|
11
|
+
'playwright.config.ts',
|
|
12
|
+
'.env.example',
|
|
13
|
+
'tests/e2e/pages/BasePage.ts',
|
|
14
|
+
'tests/e2e/fixtures/test.ts',
|
|
15
|
+
'tests/e2e/auth.setup.ts',
|
|
16
|
+
'tests/e2e/support/auth.ts',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const GITIGNORE_LINES = [
|
|
20
|
+
'# Playwright',
|
|
21
|
+
'playwright-report/',
|
|
22
|
+
'test-results/',
|
|
23
|
+
'tests/e2e/.auth/',
|
|
24
|
+
'.env',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const MCP_CONFIG = {
|
|
28
|
+
playwright: {
|
|
29
|
+
command: 'npx',
|
|
30
|
+
args: ['-y', '@playwright/mcp@latest', '--isolated', '--browser', 'chromium', '--headless'],
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Scaffold Playwright E2E harness into a target project directory.
|
|
36
|
+
* @param {string} [targetDir] Destination directory (defaults to process.cwd())
|
|
37
|
+
* @param {{ force?: boolean, silent?: boolean }} [opts]
|
|
38
|
+
* @returns {Promise<{ copied: string[], skipped: string[], mcpUpdated: boolean }>}
|
|
39
|
+
*/
|
|
40
|
+
async function scaffoldPlaywright(targetDir, opts) {
|
|
41
|
+
if (targetDir === undefined) targetDir = process.cwd();
|
|
42
|
+
var force = opts && opts.force ? true : false;
|
|
43
|
+
var silent = opts && opts.silent ? true : false;
|
|
44
|
+
var log = silent ? function () {} : console.log;
|
|
45
|
+
|
|
46
|
+
var result = { copied: [], skipped: [], mcpUpdated: false };
|
|
47
|
+
|
|
48
|
+
// ── Harness files ─────────────────────────────────────────────
|
|
49
|
+
for (var i = 0; i < HARNESS_FILES.length; i++) {
|
|
50
|
+
var relPath = HARNESS_FILES[i];
|
|
51
|
+
var src = path.join(HARNESS_SRC, relPath);
|
|
52
|
+
var dest = path.join(targetDir, relPath);
|
|
53
|
+
var exists = await fs.pathExists(dest);
|
|
54
|
+
|
|
55
|
+
if (!force && exists) {
|
|
56
|
+
log(chalk.gray(' ↷ Skipped (already exists): ' + relPath));
|
|
57
|
+
result.skipped.push(relPath);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
await fs.ensureDir(path.dirname(dest));
|
|
62
|
+
await fs.copy(src, dest, { overwrite: true });
|
|
63
|
+
log(chalk.green(' ✓ ' + (exists ? 'Overwrote' : 'Created') + ': ' + relPath));
|
|
64
|
+
result.copied.push(relPath);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── .mcp.json ─────────────────────────────────────────────────
|
|
68
|
+
var mcpFile = path.join(targetDir, '.mcp.json');
|
|
69
|
+
var mcpConfig = { mcpServers: {} };
|
|
70
|
+
if (await fs.pathExists(mcpFile)) {
|
|
71
|
+
mcpConfig = await fs.readJson(mcpFile).catch(function () { return { mcpServers: {} }; });
|
|
72
|
+
}
|
|
73
|
+
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
74
|
+
|
|
75
|
+
if (!force && mcpConfig.mcpServers.playwright) {
|
|
76
|
+
log(chalk.gray(' ↷ Skipped .mcp.json playwright entry (already exists)'));
|
|
77
|
+
} else {
|
|
78
|
+
mcpConfig.mcpServers.playwright = MCP_CONFIG.playwright;
|
|
79
|
+
await fs.writeJson(mcpFile, mcpConfig, { spaces: 2 });
|
|
80
|
+
log(chalk.green(' ✓ Updated .mcp.json with playwright MCP server'));
|
|
81
|
+
result.mcpUpdated = true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ── .gitignore ────────────────────────────────────────────────
|
|
85
|
+
var gitignoreFile = path.join(targetDir, '.gitignore');
|
|
86
|
+
var gitignoreContent = '';
|
|
87
|
+
if (await fs.pathExists(gitignoreFile)) {
|
|
88
|
+
gitignoreContent = await fs.readFile(gitignoreFile, 'utf-8');
|
|
89
|
+
}
|
|
90
|
+
var missingLines = GITIGNORE_LINES.filter(function (line) {
|
|
91
|
+
return !gitignoreContent.includes(line);
|
|
92
|
+
});
|
|
93
|
+
if (missingLines.length > 0) {
|
|
94
|
+
var addition = '\n' + missingLines.join('\n') + '\n';
|
|
95
|
+
await fs.appendFile(gitignoreFile, addition);
|
|
96
|
+
log(chalk.green(' ✓ Updated .gitignore'));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = {
|
|
103
|
+
scaffoldPlaywright: scaffoldPlaywright,
|
|
104
|
+
HARNESS_FILES: HARNESS_FILES,
|
|
105
|
+
MCP_CONFIG: MCP_CONFIG,
|
|
106
|
+
};
|
package/scripts/task.js
CHANGED
|
@@ -189,7 +189,7 @@ async function resumeTask(taskId) {
|
|
|
189
189
|
await fs.writeJson(statePath, taskState, { spaces: 2 });
|
|
190
190
|
|
|
191
191
|
console.log(chalk.green(`✓ Resumed task: ${taskId}`));
|
|
192
|
-
console.log(` ${chalk.white('Gate:')} ${taskState.currentGate} (${gateLabel(taskState.currentGate)})`);
|
|
192
|
+
console.log(` ${chalk.white('Gate:')} ${taskState.currentGate} (${gateLabel(taskState.currentGate, taskState.taskType)})`);
|
|
193
193
|
console.log(` ${chalk.white('Title:')} ${ctx.title.substring(0, 70)}`);
|
|
194
194
|
if (taskState.notes) console.log(` ${chalk.white('Note:')} ${taskState.notes}`);
|
|
195
195
|
console.log(chalk.gray('\n Open Claude to resume: claude'));
|
|
@@ -355,10 +355,11 @@ async function nextGate(taskId) {
|
|
|
355
355
|
const summaryContent = await generateMarkdownSummary(taskState);
|
|
356
356
|
await fs.writeFile(summaryPath, summaryContent, 'utf-8');
|
|
357
357
|
|
|
358
|
-
const
|
|
358
|
+
const taskType = existing.taskType || 'feature';
|
|
359
359
|
console.log(chalk.green(`✓ Gate ${currentGate} approved for ${resolvedId}.`));
|
|
360
360
|
console.log(chalk.gray(` Summary saved to: plan/${resolvedId}/task-summary.md`));
|
|
361
|
-
|
|
361
|
+
const nextLabel = gateLabel(nextGateNum, taskType);
|
|
362
|
+
console.log(chalk.white(`\n Next: Gate ${nextGateNum} — ${nextLabel}`));
|
|
362
363
|
console.log(chalk.cyan(`\n To continue in a fresh session (Recommended to avoid context pollution):`));
|
|
363
364
|
console.log(chalk.gray(` 1. Open a NEW chatbox or terminal session.`));
|
|
364
365
|
console.log(chalk.gray(` 2. Run: aiflow task resume ${resolvedId} (to load context).`));
|
|
@@ -418,21 +419,24 @@ async function detectCurrentGate(taskId) {
|
|
|
418
419
|
}
|
|
419
420
|
|
|
420
421
|
async function generateMarkdownSummary(taskState) {
|
|
422
|
+
const taskType = taskState.taskType || 'feature';
|
|
423
|
+
const maxGate = taskType === 'testing' ? 4 : 5;
|
|
421
424
|
const lines = [];
|
|
422
425
|
lines.push(`# Task Summary: ${taskState.taskId}`);
|
|
423
426
|
lines.push(`**Title:** ${taskState.title}`);
|
|
427
|
+
lines.push(`**Type:** ${taskType}`);
|
|
424
428
|
lines.push(`**Status:** ${taskState.status}`);
|
|
425
|
-
lines.push(`**Current Gate:** ${taskState.currentGate} (${gateLabel(taskState.currentGate)})`);
|
|
429
|
+
lines.push(`**Current Gate:** ${taskState.currentGate} (${gateLabel(taskState.currentGate, taskType)})`);
|
|
426
430
|
lines.push(`**Updated At:** ${new Date().toLocaleString()}`);
|
|
427
431
|
lines.push(``);
|
|
428
432
|
lines.push(`## Gate History`);
|
|
429
433
|
lines.push(`| Gate | Status | Approved At |`);
|
|
430
434
|
lines.push(`|------|--------|-------------|`);
|
|
431
|
-
for (let i = 1; i <=
|
|
435
|
+
for (let i = 1; i <= maxGate; i++) {
|
|
432
436
|
const approvedAt = taskState.gateApprovals && taskState.gateApprovals[String(i)];
|
|
433
437
|
const status = approvedAt ? '✅ Approved' : (i === taskState.currentGate ? '⏳ In Progress' : '⚪ Pending');
|
|
434
438
|
const timeStr = approvedAt ? new Date(approvedAt).toLocaleString() : '-';
|
|
435
|
-
lines.push(`| Gate ${i} (${gateLabel(i)}) | ${status} | ${timeStr} |`);
|
|
439
|
+
lines.push(`| Gate ${i} (${gateLabel(i, taskType)}) | ${status} | ${timeStr} |`);
|
|
436
440
|
}
|
|
437
441
|
lines.push(``);
|
|
438
442
|
lines.push(`---`);
|
|
@@ -440,7 +444,16 @@ async function generateMarkdownSummary(taskState) {
|
|
|
440
444
|
return lines.join('\n');
|
|
441
445
|
}
|
|
442
446
|
|
|
443
|
-
function gateLabel(n) {
|
|
447
|
+
function gateLabel(n, taskType) {
|
|
448
|
+
if (taskType === 'testing') {
|
|
449
|
+
const labels = {
|
|
450
|
+
1: 'Phân tích & Confirm',
|
|
451
|
+
2: 'Lập kế hoạch Test',
|
|
452
|
+
3: 'Thực thi & Evidence',
|
|
453
|
+
4: 'Báo cáo & Sign-off',
|
|
454
|
+
};
|
|
455
|
+
return labels[n] || `Gate ${n}`;
|
|
456
|
+
}
|
|
444
457
|
const labels = {
|
|
445
458
|
1: 'AI Analyze Requirement',
|
|
446
459
|
2: 'Implementation Plan',
|
|
@@ -494,6 +507,7 @@ module.exports.createOrActivateTaskState = async function createOrActivateTaskSt
|
|
|
494
507
|
...existing,
|
|
495
508
|
taskId,
|
|
496
509
|
title: ctx.title || '',
|
|
510
|
+
taskType: ctx.taskType || existing.taskType || 'feature',
|
|
497
511
|
status: 'active',
|
|
498
512
|
createdAt: existing.createdAt || now,
|
|
499
513
|
updatedAt: now,
|