@relipa/ai-flow-kit 0.1.1 → 0.1.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -0
- package/bin/aiflow.js +55 -2
- package/bin/ak.js +0 -0
- package/custom/mcp-presets/README.md +204 -237
- package/custom/mcp-presets/backlog.json +0 -0
- package/custom/mcp-presets/figma-desktop.json +0 -0
- package/custom/mcp-presets/figma.json +0 -0
- package/custom/mcp-presets/gitnexus.json +0 -0
- package/custom/mcp-presets/google-sheets.json +0 -0
- package/custom/mcp-presets/jira.json +0 -0
- package/custom/prompts/bug-fix.md +0 -0
- package/custom/prompts/feature.md +0 -0
- package/custom/prompts/investigation.md +0 -0
- package/custom/rules/code-style.md +0 -0
- package/custom/rules/java/code-style.md +0 -0
- package/custom/rules/java/naming.md +0 -0
- package/custom/rules/java/review-checklist.md +0 -0
- package/custom/rules/java/spring-boot-examples.md +0 -0
- package/custom/rules/javascript/code-style.md +0 -0
- package/custom/rules/javascript/naming.md +0 -0
- package/custom/rules/ml-conventions.md +88 -0
- package/custom/rules/ml-review-checklist.md +14 -0
- package/custom/rules/naming.md +0 -0
- package/custom/rules/php/code-style.md +0 -0
- package/custom/rules/php/naming.md +0 -0
- package/custom/rules/project-conventions.md +0 -0
- package/custom/rules/review-checklist.md +0 -0
- package/custom/skills/deploy-model/SKILL.md +90 -0
- package/custom/skills/design-experiment/SKILL.md +88 -0
- package/custom/skills/evaluate-model/SKILL.md +93 -0
- package/custom/skills/explore-data/SKILL.md +87 -0
- package/custom/skills/figma-to-component/SKILL.md +0 -0
- package/custom/skills/frame-ml-problem/SKILL.md +88 -0
- package/custom/skills/generate-spec/SKILL.md +0 -0
- package/custom/skills/impact-analysis/SKILL.md +0 -0
- package/custom/skills/improve-algorithm/SKILL.md +74 -0
- package/custom/skills/investigate-bug/SKILL.md +0 -0
- package/custom/skills/read-study-requirement/SKILL.md +0 -0
- package/custom/skills/report-customer/SKILL.md +0 -0
- package/custom/skills/review-plan/SKILL.md +0 -0
- package/custom/skills/train-model/SKILL.md +78 -0
- package/custom/templates/laravel.md +0 -0
- package/custom/templates/nestjs.md +0 -0
- package/custom/templates/nextjs.md +0 -0
- package/custom/templates/nodejs-express.md +0 -0
- package/custom/templates/php-plain.md +0 -0
- package/custom/templates/php.md +0 -0
- package/custom/templates/python-django.md +0 -0
- package/custom/templates/python-fastapi.md +0 -0
- package/custom/templates/python-ml.md +273 -0
- package/custom/templates/python.md +0 -0
- package/custom/templates/reactjs.md +0 -0
- package/custom/templates/shared/gate-workflow.md +0 -0
- package/custom/templates/shared/ml-gate-workflow.md +103 -0
- package/custom/templates/spring-boot.md +0 -0
- package/custom/templates/tools/claude.md +0 -0
- package/custom/templates/tools/copilot.md +0 -0
- package/custom/templates/tools/cursor.md +0 -0
- package/custom/templates/tools/gemini.md +0 -0
- package/custom/templates/tools/generic.md +0 -0
- package/custom/templates/vue-nuxt.md +0 -0
- package/docs/common/AIFLOW.md +512 -512
- package/docs/common/CHANGELOG.md +0 -0
- package/docs/common/QUICK_START.md +0 -0
- package/docs/common/ai-integration.md +0 -0
- package/docs/common/cli-reference.md +0 -0
- package/docs/common/configuration.md +0 -0
- package/docs/common/getting-started.md +0 -0
- package/docs/common/troubleshooting.md +0 -0
- package/docs/common/workflows/bug-fix.md +0 -0
- package/docs/common/workflows/feature.md +0 -0
- package/docs/common/workflows/figma.md +0 -0
- package/docs/common/workflows/impact-analysis.md +0 -0
- package/docs/common/workflows/investigation.md +0 -0
- package/docs/common/workflows/refactor.md +0 -0
- package/docs/project/ARCHITECTURE.md +0 -0
- package/index.js +0 -0
- package/package.json +1 -1
- package/scripts/checkpoint.js +0 -0
- package/scripts/config.js +0 -0
- package/scripts/context.js +0 -0
- package/scripts/create-score-excel.js +0 -0
- package/scripts/detect.js +239 -239
- package/scripts/doctor.js +192 -218
- package/scripts/gitnexus-worker.js +0 -0
- package/scripts/guide.js +0 -0
- package/scripts/hooks/block-git-write.js +0 -0
- package/scripts/hooks/session-start.js +0 -0
- package/scripts/hooks/session-stop.js +0 -0
- package/scripts/init.js +1112 -1009
- package/scripts/link-resolver.js +0 -0
- package/scripts/memory.js +0 -0
- package/scripts/prompt.js +538 -538
- package/scripts/remove.js +0 -0
- package/scripts/task.js +546 -546
- package/scripts/telemetry/cli.js +0 -0
- package/scripts/telemetry/config.js +0 -0
- package/scripts/telemetry/crypto.js +0 -0
- package/scripts/telemetry/flush.js +0 -0
- package/scripts/telemetry/record.js +0 -0
- package/scripts/update.js +10 -7
- package/scripts/use.js +33 -12
- package/scripts/validate.js +0 -0
- package/upstream/.claude-plugin/marketplace.json +0 -0
- package/upstream/.claude-plugin/plugin.json +0 -0
- package/upstream/.codex/INSTALL.md +0 -0
- package/upstream/.cursor-plugin/plugin.json +0 -0
- package/upstream/.gitattributes +0 -0
- package/upstream/.github/FUNDING.yml +0 -0
- package/upstream/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- package/upstream/.github/ISSUE_TEMPLATE/config.yml +0 -0
- package/upstream/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- package/upstream/.github/ISSUE_TEMPLATE/platform_support.md +0 -0
- package/upstream/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- package/upstream/.opencode/INSTALL.md +0 -0
- package/upstream/.opencode/plugins/superpowers.js +0 -0
- package/upstream/.version-bump.json +0 -0
- package/upstream/AGENTS.md +0 -0
- package/upstream/CHANGELOG.md +0 -0
- package/upstream/CLAUDE.md +0 -0
- package/upstream/CODE_OF_CONDUCT.md +0 -0
- package/upstream/GEMINI.md +0 -0
- package/upstream/LICENSE +0 -0
- package/upstream/README.md +0 -0
- package/upstream/RELEASE-NOTES.md +0 -0
- package/upstream/agents/code-reviewer.md +0 -0
- package/upstream/commands/brainstorm.md +0 -0
- package/upstream/commands/execute-plan.md +0 -0
- package/upstream/commands/write-plan.md +0 -0
- package/upstream/docs/README.codex.md +0 -0
- package/upstream/docs/README.opencode.md +0 -0
- package/upstream/docs/plans/2025-11-22-opencode-support-design.md +0 -0
- package/upstream/docs/plans/2025-11-22-opencode-support-implementation.md +0 -0
- package/upstream/docs/plans/2025-11-28-skills-improvements-from-user-feedback.md +0 -0
- package/upstream/docs/plans/2026-01-17-visual-brainstorming.md +0 -0
- package/upstream/docs/superpowers/plans/2026-01-22-document-review-system.md +0 -0
- package/upstream/docs/superpowers/plans/2026-02-19-visual-brainstorming-refactor.md +0 -0
- package/upstream/docs/superpowers/plans/2026-03-11-zero-dep-brainstorm-server.md +0 -0
- package/upstream/docs/superpowers/plans/2026-03-23-codex-app-compatibility.md +0 -0
- package/upstream/docs/superpowers/specs/2026-01-22-document-review-system-design.md +0 -0
- package/upstream/docs/superpowers/specs/2026-02-19-visual-brainstorming-refactor-design.md +0 -0
- package/upstream/docs/superpowers/specs/2026-03-11-zero-dep-brainstorm-server-design.md +0 -0
- package/upstream/docs/superpowers/specs/2026-03-23-codex-app-compatibility-design.md +0 -0
- package/upstream/docs/testing.md +0 -0
- package/upstream/docs/windows/polyglot-hooks.md +0 -0
- package/upstream/gemini-extension.json +0 -0
- package/upstream/hooks/hooks-cursor.json +0 -0
- package/upstream/hooks/hooks.json +0 -0
- package/upstream/hooks/run-hook.cmd +0 -0
- package/upstream/hooks/session-start +0 -0
- package/upstream/package.json +0 -0
- package/upstream/scripts/bump-version.sh +0 -0
- package/upstream/skills/brainstorming/SKILL.md +0 -0
- package/upstream/skills/brainstorming/scripts/frame-template.html +0 -0
- package/upstream/skills/brainstorming/scripts/helper.js +0 -0
- package/upstream/skills/brainstorming/scripts/server.cjs +0 -0
- package/upstream/skills/brainstorming/scripts/start-server.sh +0 -0
- package/upstream/skills/brainstorming/scripts/stop-server.sh +0 -0
- package/upstream/skills/brainstorming/spec-document-reviewer-prompt.md +0 -0
- package/upstream/skills/brainstorming/visual-companion.md +0 -0
- package/upstream/skills/dispatching-parallel-agents/SKILL.md +0 -0
- package/upstream/skills/executing-plans/SKILL.md +0 -0
- package/upstream/skills/finishing-a-development-branch/SKILL.md +0 -0
- package/upstream/skills/receiving-code-review/SKILL.md +0 -0
- package/upstream/skills/requesting-code-review/SKILL.md +0 -0
- package/upstream/skills/requesting-code-review/code-reviewer.md +0 -0
- package/upstream/skills/subagent-driven-development/SKILL.md +0 -0
- package/upstream/skills/subagent-driven-development/code-quality-reviewer-prompt.md +0 -0
- package/upstream/skills/subagent-driven-development/implementer-prompt.md +0 -0
- package/upstream/skills/subagent-driven-development/spec-reviewer-prompt.md +0 -0
- package/upstream/skills/systematic-debugging/CREATION-LOG.md +0 -0
- package/upstream/skills/systematic-debugging/SKILL.md +0 -0
- package/upstream/skills/systematic-debugging/condition-based-waiting-example.ts +0 -0
- package/upstream/skills/systematic-debugging/condition-based-waiting.md +0 -0
- package/upstream/skills/systematic-debugging/defense-in-depth.md +0 -0
- package/upstream/skills/systematic-debugging/find-polluter.sh +0 -0
- package/upstream/skills/systematic-debugging/root-cause-tracing.md +0 -0
- package/upstream/skills/systematic-debugging/test-academic.md +0 -0
- package/upstream/skills/systematic-debugging/test-pressure-1.md +0 -0
- package/upstream/skills/systematic-debugging/test-pressure-2.md +0 -0
- package/upstream/skills/systematic-debugging/test-pressure-3.md +0 -0
- package/upstream/skills/tdd-lean/SKILL.md +0 -0
- package/upstream/skills/test-driven-development/SKILL.md +0 -0
- package/upstream/skills/test-driven-development/testing-anti-patterns.md +0 -0
- package/upstream/skills/using-git-worktrees/SKILL.md +0 -0
- package/upstream/skills/using-superpowers/SKILL.md +0 -0
- package/upstream/skills/using-superpowers/references/codex-tools.md +0 -0
- package/upstream/skills/using-superpowers/references/copilot-tools.md +0 -0
- package/upstream/skills/using-superpowers/references/gemini-tools.md +0 -0
- package/upstream/skills/verification-before-completion/SKILL.md +0 -0
- package/upstream/skills/writing-plans/SKILL.md +0 -0
- package/upstream/skills/writing-plans/plan-document-reviewer-prompt.md +0 -0
- package/upstream/skills/writing-skills/SKILL.md +0 -0
- package/upstream/skills/writing-skills/anthropic-best-practices.md +0 -0
- package/upstream/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -0
- package/upstream/skills/writing-skills/graphviz-conventions.dot +0 -0
- package/upstream/skills/writing-skills/persuasion-principles.md +0 -0
- package/upstream/skills/writing-skills/render-graphs.js +0 -0
- package/upstream/skills/writing-skills/testing-skills-with-subagents.md +0 -0
- package/upstream/tests/brainstorm-server/package-lock.json +0 -0
- package/upstream/tests/brainstorm-server/package.json +0 -0
- package/upstream/tests/brainstorm-server/server.test.js +0 -0
- package/upstream/tests/brainstorm-server/windows-lifecycle.test.sh +0 -0
- package/upstream/tests/brainstorm-server/ws-protocol.test.js +0 -0
- package/upstream/tests/claude-code/README.md +0 -0
- package/upstream/tests/claude-code/analyze-token-usage.py +0 -0
- package/upstream/tests/claude-code/run-skill-tests.sh +0 -0
- package/upstream/tests/claude-code/test-document-review-system.sh +0 -0
- package/upstream/tests/claude-code/test-helpers.sh +0 -0
- package/upstream/tests/claude-code/test-subagent-driven-development-integration.sh +0 -0
- package/upstream/tests/claude-code/test-subagent-driven-development.sh +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/action-oriented.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/after-planning-flow.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/claude-suggested-it.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/i-know-what-sdd-means.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/mid-conversation-execute-plan.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/please-use-brainstorming.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/skip-formalities.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/subagent-driven-development-please.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/use-systematic-debugging.txt +0 -0
- package/upstream/tests/explicit-skill-requests/run-all.sh +0 -0
- package/upstream/tests/explicit-skill-requests/run-claude-describes-sdd.sh +0 -0
- package/upstream/tests/explicit-skill-requests/run-extended-multiturn-test.sh +0 -0
- package/upstream/tests/explicit-skill-requests/run-haiku-test.sh +0 -0
- package/upstream/tests/explicit-skill-requests/run-multiturn-test.sh +0 -0
- package/upstream/tests/explicit-skill-requests/run-test.sh +0 -0
- package/upstream/tests/opencode/run-tests.sh +0 -0
- package/upstream/tests/opencode/setup.sh +0 -0
- package/upstream/tests/opencode/test-plugin-loading.sh +0 -0
- package/upstream/tests/opencode/test-priority.sh +0 -0
- package/upstream/tests/opencode/test-tools.sh +0 -0
- package/upstream/tests/skill-triggering/prompts/dispatching-parallel-agents.txt +0 -0
- package/upstream/tests/skill-triggering/prompts/executing-plans.txt +0 -0
- package/upstream/tests/skill-triggering/prompts/requesting-code-review.txt +0 -0
- package/upstream/tests/skill-triggering/prompts/systematic-debugging.txt +0 -0
- package/upstream/tests/skill-triggering/prompts/test-driven-development.txt +0 -0
- package/upstream/tests/skill-triggering/prompts/writing-plans.txt +0 -0
- package/upstream/tests/skill-triggering/run-all.sh +0 -0
- package/upstream/tests/skill-triggering/run-test.sh +0 -0
- package/upstream/tests/subagent-driven-dev/go-fractals/design.md +0 -0
- package/upstream/tests/subagent-driven-dev/go-fractals/plan.md +0 -0
- package/upstream/tests/subagent-driven-dev/go-fractals/scaffold.sh +0 -0
- package/upstream/tests/subagent-driven-dev/run-test.sh +0 -0
- package/upstream/tests/subagent-driven-dev/svelte-todo/design.md +0 -0
- package/upstream/tests/subagent-driven-dev/svelte-todo/plan.md +0 -0
- package/upstream/tests/subagent-driven-dev/svelte-todo/scaffold.sh +0 -0
- package/custom/mcp-presets/playwright.json +0 -11
- package/custom/rules/test-patterns.md +0 -105
- package/custom/skills/automation-testing/SKILL.md +0 -168
- package/custom/skills/automation-testing/templates/BasePage.ts +0 -30
- package/custom/skills/automation-testing/templates/playwright.config.ts +0 -21
- package/custom/skills/automation-testing/templates/repos.json +0 -8
- package/scripts/score_members.js +0 -320
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# E2E Test Patterns (Playwright)
|
|
2
|
-
|
|
3
|
-
Reusable patterns the `automation-testing` skill applies when generating Page Objects and specs. Selector priority is always: **`getByRole` → `getByLabel` → `getByTestId`** → (CSS/XPath only as last resort).
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 1. Login / Authentication
|
|
8
|
-
|
|
9
|
-
**Test cases to cover:**
|
|
10
|
-
- Valid credentials → redirect to landing page
|
|
11
|
-
- Wrong password → error message visible
|
|
12
|
-
- Empty required field → validation message
|
|
13
|
-
- (optional) Locked/disabled account → blocked
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
test('TC01 - Login success', async ({ page }) => {
|
|
17
|
-
const login = new LoginPage(page);
|
|
18
|
-
await login.navigate('/login');
|
|
19
|
-
await login.login('user@example.com', 'Password123');
|
|
20
|
-
await expect(page).toHaveURL(/\/dashboard/);
|
|
21
|
-
});
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## 2. CRUD (Create / Read / Update / Delete)
|
|
27
|
-
|
|
28
|
-
**Cover:** create → appears in list → edit → reflects change → delete → removed. Always assert on the list/detail after each mutation, not just the success toast.
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
test('Create item appears in list', async ({ page }) => {
|
|
32
|
-
const list = new ItemListPage(page);
|
|
33
|
-
await list.navigate('/items');
|
|
34
|
-
await list.openCreate();
|
|
35
|
-
await list.fillForm({ name: 'Sample' });
|
|
36
|
-
await list.submit();
|
|
37
|
-
await expect(list.rowByName('Sample')).toBeVisible();
|
|
38
|
-
});
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
> **Test data:** prefer a unique suffix (e.g. timestamp passed in via fixture) so reruns don't collide. Clean up created data in `afterEach` when possible.
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## 3. Form validation
|
|
46
|
-
|
|
47
|
-
- Submit empty → each required field shows its message
|
|
48
|
-
- Invalid format (email, phone) → format message
|
|
49
|
-
- Boundary (min/max length) → length message
|
|
50
|
-
- Valid input → no error, submit succeeds
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
test('Email format validation', async ({ page }) => {
|
|
54
|
-
const form = new SignupPage(page);
|
|
55
|
-
await form.navigate('/signup');
|
|
56
|
-
await form.emailInput.fill('not-an-email');
|
|
57
|
-
await form.submit();
|
|
58
|
-
await expect(page.getByText(/invalid email/i)).toBeVisible();
|
|
59
|
-
});
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## 4. Modal / Dialog
|
|
65
|
-
|
|
66
|
-
- Open trigger → modal visible
|
|
67
|
-
- Confirm → action runs + modal closes
|
|
68
|
-
- Cancel / backdrop / Esc → modal closes, no action
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
await page.getByRole('button', { name: 'Delete' }).click();
|
|
72
|
-
const dialog = page.getByRole('dialog');
|
|
73
|
-
await expect(dialog).toBeVisible();
|
|
74
|
-
await dialog.getByRole('button', { name: 'Confirm' }).click();
|
|
75
|
-
await expect(dialog).toBeHidden();
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## 5. Pagination / Table
|
|
81
|
-
|
|
82
|
-
- Default page loads expected count
|
|
83
|
-
- Next/Prev changes rows
|
|
84
|
-
- Page size change updates count
|
|
85
|
-
- Sort by column reorders rows
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
await expect(page.getByRole('row')).toHaveCount(11); // header + 10 rows
|
|
89
|
-
await page.getByRole('button', { name: 'Next' }).click();
|
|
90
|
-
await expect(page.getByText('Page 2')).toBeVisible();
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## 6. General assertions & waits
|
|
96
|
-
|
|
97
|
-
- Prefer **web-first assertions** (`await expect(locator).toBeVisible()`) — they auto-retry. Never `waitForTimeout` to "wait for UI".
|
|
98
|
-
- Assert on **user-visible outcome** (URL, text, element state), not internal state.
|
|
99
|
-
- For async data, assert the loaded element, not a spinner disappearing.
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
## 7. Evidence (MCP explore phase)
|
|
104
|
-
|
|
105
|
-
When exploring via Playwright MCP, capture a screenshot per significant step into `<evidenceDir>/[ticket-id]/TC<NN>-step<N>.png`. Name files so the reviewer can map evidence → test case at a glance.
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: automation-testing
|
|
3
|
-
description: Generate runnable Playwright E2E tests for a web UI from requirement/business-logic docs or a tester's free-form draft. Uses Playwright MCP to explore real selectors and capture evidence on a live browser, then emits durable .spec.ts files plus an HTML report. Tester does not need to know TypeScript.
|
|
4
|
-
keywords: automation, testing, playwright, e2e, mcp, testcase, browser, evidence, report, qa, tester
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Automation Testing (Hybrid MCP)
|
|
8
|
-
|
|
9
|
-
Generate **runnable** Playwright E2E tests for a web UI. Works in a **multi-repo workspace** (a parent folder containing backend + frontend repos).
|
|
10
|
-
|
|
11
|
-
## Core principle — HYBRID
|
|
12
|
-
|
|
13
|
-
> Playwright MCP does **not** replace test files. It **improves** them.
|
|
14
|
-
> Use the live browser to *learn* the app's real selectors and capture evidence, but the deliverable is always committable `.spec.ts` so CI can re-run.
|
|
15
|
-
|
|
16
|
-
## When to use
|
|
17
|
-
|
|
18
|
-
- Tester/Dev wants E2E UI tests but doesn't write TypeScript
|
|
19
|
-
- A requirement (`requirement.md`) or business-logic doc exists and test cases should be derived from it
|
|
20
|
-
- A finished feature needs regression coverage with evidence
|
|
21
|
-
|
|
22
|
-
## How to invoke
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
/automation-testing baseUrl: <url> [explore: true|false] [autoRun: true|false] [headed: true|false] [ticketId: <id>]
|
|
26
|
-
---
|
|
27
|
-
(optional) tester's free-form test case draft
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
**Example:**
|
|
31
|
-
```
|
|
32
|
-
/automation-testing baseUrl: http://localhost:3000 explore: true autoRun: true
|
|
33
|
-
---
|
|
34
|
-
1. Vào /login, nhập đúng email/password → vào dashboard
|
|
35
|
-
2. Nhập sai password → hiện thông báo lỗi
|
|
36
|
-
3. Để trống email → hiện validation lỗi
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Params
|
|
40
|
-
|
|
41
|
-
| Param | Type | Default | Description |
|
|
42
|
-
|-------|------|---------|-------------|
|
|
43
|
-
| `baseUrl` | string | — | URL of the running web app **(required)** |
|
|
44
|
-
| `explore` | boolean | `true` | Use Playwright MCP to explore real selectors + capture evidence before generating |
|
|
45
|
-
| `autoRun` | boolean | `true` | Run `npx playwright test` after generating |
|
|
46
|
-
| `headed` | boolean | `false` | Show browser window during `autoRun` (CI should keep `false`) |
|
|
47
|
-
| `ticketId` | string | — | Ticket id; if omitted, read from `.aiflow/context/current.json` |
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## Phase 1 — Collect
|
|
52
|
-
|
|
53
|
-
1. **Resolve workspace.** Read `.aiflow/repos.json`.
|
|
54
|
-
- If it does NOT exist: copy the template at `.claude/skills/automation-testing/templates/repos.json`, ask the user to confirm the paths (`backend`, `frontend`, `e2e`, `planDir`, `evidenceDir`), then write `.aiflow/repos.json`. **Do not guess** the folder layout.
|
|
55
|
-
2. **Resolve ticket.** Use `ticketId` param, else read `.aiflow/context/current.json`. If none, use a descriptive slug for `[ticket-id]`.
|
|
56
|
-
3. **Read inputs** (any that exist):
|
|
57
|
-
- `<planDir>/[ticket-id]/requirement.md` (Gate 1)
|
|
58
|
-
- `<planDir>/[ticket-id]/plan.md` (Gate 2)
|
|
59
|
-
- business-logic doc (path provided by user or pasted)
|
|
60
|
-
- tester's draft (text after `---`)
|
|
61
|
-
- `custom/rules/test-patterns.md`
|
|
62
|
-
4. **Require `baseUrl`.** If missing, ask before continuing.
|
|
63
|
-
5. If NO testcase draft is provided, that's fine — derive test cases from the requirement + business-logic docs (this is the primary value).
|
|
64
|
-
|
|
65
|
-
---
|
|
66
|
-
|
|
67
|
-
## Phase 2 — Analyze (GATE 1)
|
|
68
|
-
|
|
69
|
-
Produce a structured test case list:
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
## Test Cases — [Feature Name]
|
|
73
|
-
|
|
74
|
-
| ID | Test case | Steps | Expected Result |
|
|
75
|
-
|------|--------------------------------|------------------------------------------------|----------------------------------|
|
|
76
|
-
| TC01 | Đăng nhập thành công | 1. /login 2. Nhập email+pass hợp lệ 3. Submit | Redirect /dashboard |
|
|
77
|
-
| TC02 | Sai mật khẩu | 1. /login 2. Sai password 3. Submit | Hiện error message |
|
|
78
|
-
| TC03 | Validation email trống | 1. /login 2. Submit ngay | "Email là bắt buộc" |
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
> ⚠️ **GATE — STOP HERE.** Show the list and ask:
|
|
82
|
-
> "Danh sách test cases trên có đúng không? Thêm/sửa/xóa gì không? Xác nhận để tôi khám phá app và sinh code."
|
|
83
|
-
>
|
|
84
|
-
> **Wait for confirmation before Phase 3.** Do not explore or generate code until confirmed.
|
|
85
|
-
|
|
86
|
-
---
|
|
87
|
-
|
|
88
|
-
## Phase 3 — Explore (Playwright MCP, live browser)
|
|
89
|
-
|
|
90
|
-
Only if `explore: true` AND the Playwright MCP server is available AND the app is reachable.
|
|
91
|
-
|
|
92
|
-
For each test case, drive the real app:
|
|
93
|
-
1. `browser_navigate(baseUrl + path)` — open the page
|
|
94
|
-
2. `browser_snapshot()` — read the accessibility tree; **extract real selectors** (prefer `getByRole` → `getByLabel` → `getByTestId`)
|
|
95
|
-
3. Walk the flow (type, click) to confirm it behaves as the test case expects
|
|
96
|
-
4. `browser_take_screenshot()` → save to `<evidenceDir>/[ticket-id]/TC<NN>-step<N>.png`
|
|
97
|
-
5. Record the verified selectors for Phase 4
|
|
98
|
-
|
|
99
|
-
**Output of this phase:** a selector map (element → verified locator) + evidence screenshots.
|
|
100
|
-
|
|
101
|
-
> Do NOT commit the MCP session. The committable artifact is produced in Phase 4.
|
|
102
|
-
|
|
103
|
-
### Fallback
|
|
104
|
-
If `explore: false`, MCP unavailable, or the app is unreachable:
|
|
105
|
-
- Generate from inference (v1 behavior)
|
|
106
|
-
- **Warn clearly** that selectors are unverified and a developer must check them
|
|
107
|
-
- Skip evidence capture
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
## Phase 4 — Generate (durable scripts)
|
|
112
|
-
|
|
113
|
-
Write files under the `e2e` path from `repos.json`:
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
<e2e>/
|
|
117
|
-
├── pages/
|
|
118
|
-
│ ├── BasePage.ts ← only if missing (template below)
|
|
119
|
-
│ └── [Feature]Page.ts ← Page Object; selectors from Phase 3
|
|
120
|
-
├── specs/[ticket-id]/[feature].spec.ts
|
|
121
|
-
├── fixtures/test.ts ← only if missing
|
|
122
|
-
└── playwright.config.ts ← only if missing (template below)
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
**Rules:**
|
|
126
|
-
- `BasePage.ts` / `playwright.config.ts` / `fixtures/test.ts` — create only if absent. Templates at `.claude/skills/automation-testing/templates/`.
|
|
127
|
-
- **Page Object** — declare locators using verified selectors from Phase 3 (`getByRole` > `getByLabel` > `getByTestId`; avoid CSS/XPath unless unavoidable). One async method per action.
|
|
128
|
-
- **Spec** — one `test()` per case; group with `test.describe`; test ids MUST match Phase 2 (TC01, TC02…); explicit `expect` assertions.
|
|
129
|
-
- Apply patterns from `custom/rules/test-patterns.md` where relevant.
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
## Phase 5 — Run & Report
|
|
134
|
-
|
|
135
|
-
### If `autoRun: true`
|
|
136
|
-
1. Determine the target repo for E2E (per §Git below) and `cd` there.
|
|
137
|
-
2. Run `npx playwright test <e2e>/specs/[ticket-id]/` (add `--headed` if `headed: true`).
|
|
138
|
-
3. On completion: `npx playwright show-report` (or report the HTML path).
|
|
139
|
-
4. Summarize: pass/fail count, failed-test screenshots, plus the Phase 3 exploration evidence folder.
|
|
140
|
-
|
|
141
|
-
### If `autoRun: false`
|
|
142
|
-
Print manual instructions:
|
|
143
|
-
```
|
|
144
|
-
✅ Scripts generated.
|
|
145
|
-
npx playwright test # all
|
|
146
|
-
npx playwright test <e2e>/specs/[ticket-id]/ # this ticket
|
|
147
|
-
npx playwright test --headed # show browser
|
|
148
|
-
npx playwright show-report # HTML report
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
## Git & Gate 5 (multi-repo)
|
|
154
|
-
|
|
155
|
-
The parent workspace is usually NOT a git repo. Commit to the **repo that owns the code under test**:
|
|
156
|
-
|
|
157
|
-
| Artifact | Commit target |
|
|
158
|
-
|----------|---------------|
|
|
159
|
-
| E2E tests for UI | the **frontend** repo (`<frontend>/e2e/`) — unless a dedicated `e2e` repo exists |
|
|
160
|
-
| requirement.md / plan.md | `<planDir>` at parent level (shared; not committed into a sub-repo) |
|
|
161
|
-
|
|
162
|
-
> When creating a PR (aiflow Gate 5 / `requesting-code-review`), `cd` into the correct sub-repo first — `git`/PR commands must run inside that repo, not the parent cwd.
|
|
163
|
-
|
|
164
|
-
---
|
|
165
|
-
|
|
166
|
-
## Two mandatory gates
|
|
167
|
-
1. After Phase 2 — confirm the test case list before exploring/generating.
|
|
168
|
-
2. After Phase 5 — hand off (evidence + report) and ask the developer to review before commit/PR.
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { Page, expect } from '@playwright/test';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* BasePage — shared helpers for all Page Objects.
|
|
5
|
-
* Generated by the aiflow `automation-testing` skill. Create once; extend per feature.
|
|
6
|
-
*/
|
|
7
|
-
export abstract class BasePage {
|
|
8
|
-
constructor(protected readonly page: Page) {}
|
|
9
|
-
|
|
10
|
-
/** Navigate to a path relative to playwright.config `baseURL`. */
|
|
11
|
-
async navigate(path = '/'): Promise<void> {
|
|
12
|
-
await this.page.goto(path);
|
|
13
|
-
await this.waitForLoad();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Wait until network is idle (page settled). */
|
|
17
|
-
async waitForLoad(): Promise<void> {
|
|
18
|
-
await this.page.waitForLoadState('networkidle');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Capture a full-page screenshot as evidence. */
|
|
22
|
-
async screenshot(name: string): Promise<void> {
|
|
23
|
-
await this.page.screenshot({ path: `evidence/${name}.png`, fullPage: true });
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** Assert the current URL matches (string or RegExp). */
|
|
27
|
-
async expectUrl(url: string | RegExp): Promise<void> {
|
|
28
|
-
await expect(this.page).toHaveURL(url);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { defineConfig, devices } from '@playwright/test';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Playwright config generated by the aiflow `automation-testing` skill.
|
|
5
|
-
* Create once per target repo; edit to fit your environment.
|
|
6
|
-
*/
|
|
7
|
-
export default defineConfig({
|
|
8
|
-
testDir: './specs',
|
|
9
|
-
fullyParallel: false,
|
|
10
|
-
retries: process.env.CI ? 1 : 0,
|
|
11
|
-
reporter: [['html', { open: 'never' }], ['list']],
|
|
12
|
-
use: {
|
|
13
|
-
baseURL: process.env.BASE_URL || 'http://localhost:3000',
|
|
14
|
-
trace: 'on-first-retry',
|
|
15
|
-
screenshot: 'only-on-failure',
|
|
16
|
-
video: 'retain-on-failure',
|
|
17
|
-
},
|
|
18
|
-
projects: [
|
|
19
|
-
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
20
|
-
],
|
|
21
|
-
});
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"_comment": "Workspace map for the automation-testing skill. Copied to .aiflow/repos.json. Paths are relative to the parent workspace folder (aiflow cwd). Adjust to match your layout.",
|
|
3
|
-
"backend": "./backend",
|
|
4
|
-
"frontend": "./frontend",
|
|
5
|
-
"e2e": "./frontend/e2e",
|
|
6
|
-
"planDir": "./plan",
|
|
7
|
-
"evidenceDir": "./frontend/e2e/evidence"
|
|
8
|
-
}
|
package/scripts/score_members.js
DELETED
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const XLSX = require('xlsx');
|
|
7
|
-
|
|
8
|
-
const LOG_FILE = path.resolve(__dirname, '../tellog/log_25-05-2026_31-05-2026');
|
|
9
|
-
const OUTPUT_FILE = path.resolve(__dirname, '../tellog/log_25-05-2026_31-05-2026.xlsx');
|
|
10
|
-
|
|
11
|
-
const MEMBERS = [
|
|
12
|
-
'anhtt@relipasoft.com',
|
|
13
|
-
'hoangdv@relipasoft.com',
|
|
14
|
-
'chinhtt@relipasoft.com',
|
|
15
|
-
'phongnx@relipasoft.com',
|
|
16
|
-
'dungha@relipasoft.com',
|
|
17
|
-
'thangnv@relipasoft.com',
|
|
18
|
-
'nguyenlt@relipasoft.com',
|
|
19
|
-
'thuongvv@relipasoft.com',
|
|
20
|
-
'yenvtb@relipasoft.com',
|
|
21
|
-
'tructh@relipasoft.com',
|
|
22
|
-
'thainq@relipasoft.com',
|
|
23
|
-
'tuoittx@relipasoft.com',
|
|
24
|
-
'hungdv@relipasoft.com',
|
|
25
|
-
'hiepnk@relipasoft.com',
|
|
26
|
-
'khoihn@relipasoft.com',
|
|
27
|
-
'huynq@relipasoft.com',
|
|
28
|
-
'thinhvq@relipasoft.com',
|
|
29
|
-
'banv@relipasoft.com',
|
|
30
|
-
'duongnt1@relipasoft.com',
|
|
31
|
-
'anhnt@relipasoft.com',
|
|
32
|
-
'khanhdd@relipasoft.com',
|
|
33
|
-
'hieunv@relipasoft.com',
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
const EXPLORE_COMMANDS = new Set(['init', 'update', 'version', 'help', 'init.help', 'unknown_flag']);
|
|
37
|
-
const USE_COMMANDS = new Set(['use', 'prompt', 'guide', 'guideline', 'guiline', 'guide?']);
|
|
38
|
-
|
|
39
|
-
// Tickets that are clearly not real work (test, demo, throwaway)
|
|
40
|
-
const FAKE_TICKET_PATTERNS = /^(test|test-direct|demo|investigation|sample|trial|dummy)(-.*)?$/i;
|
|
41
|
-
|
|
42
|
-
function isRealTicket(ticket) {
|
|
43
|
-
if (!ticket || ticket === '-' || ticket.length < 3) return false;
|
|
44
|
-
if (FAKE_TICKET_PATTERNS.test(ticket)) return false;
|
|
45
|
-
// Real tickets typically follow PROJECT-ID pattern with uppercase prefix
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function parseLog() {
|
|
50
|
-
const content = fs.readFileSync(LOG_FILE, 'utf8');
|
|
51
|
-
const lines = content.split('\n');
|
|
52
|
-
|
|
53
|
-
// Skip header (line 0)
|
|
54
|
-
const events = [];
|
|
55
|
-
for (let i = 1; i < lines.length; i++) {
|
|
56
|
-
const line = lines[i].trim();
|
|
57
|
-
if (!line) continue;
|
|
58
|
-
|
|
59
|
-
// TSV format: columns separated by tabs
|
|
60
|
-
const cols = line.split('\t');
|
|
61
|
-
if (cols.length < 7) continue;
|
|
62
|
-
|
|
63
|
-
const timestamp = cols[0];
|
|
64
|
-
const repo = cols[1];
|
|
65
|
-
const user = cols[2];
|
|
66
|
-
const command = (cols[6] || '').trim();
|
|
67
|
-
const gate = (cols[8] || '').trim();
|
|
68
|
-
const ticket = (cols[9] || '').trim();
|
|
69
|
-
|
|
70
|
-
if (!timestamp || !user) continue;
|
|
71
|
-
|
|
72
|
-
// Parse date from "DD/MM/YYYY HH:MM" format
|
|
73
|
-
const dateMatch = timestamp.match(/^(\d{2})\/(\d{2})\/(\d{4})/);
|
|
74
|
-
if (!dateMatch) continue;
|
|
75
|
-
const dateStr = `${dateMatch[3]}-${dateMatch[2]}-${dateMatch[1]}`;
|
|
76
|
-
|
|
77
|
-
events.push({ timestamp, dateStr, repo, user, command, gate, ticket });
|
|
78
|
-
}
|
|
79
|
-
return events;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function detectSessions(userEvents) {
|
|
83
|
-
// A new session = gap of more than 30 minutes between consecutive events
|
|
84
|
-
// or a new day; simplified: count distinct hour blocks per day
|
|
85
|
-
const sessionKeys = new Set();
|
|
86
|
-
for (const e of userEvents) {
|
|
87
|
-
const hourMatch = e.timestamp.match(/(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):/);
|
|
88
|
-
if (hourMatch) {
|
|
89
|
-
sessionKeys.add(`${hourMatch[3]}-${hourMatch[2]}-${hourMatch[1]}-${hourMatch[4]}`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return sessionKeys.size;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function score(stats) {
|
|
96
|
-
const { totalEvents, activeDays, sessions, gateStarts, gateApproveds, ticketsProcessed,
|
|
97
|
-
ticketsInWeek, hasUseOrPrompt, hasGateStartWithTicket, hasGate3to5Approved } = stats;
|
|
98
|
-
|
|
99
|
-
if (totalEvents === 0) return { depth: 0, depthLabel: 'Không hoạt động' };
|
|
100
|
-
|
|
101
|
-
const hasOnlyExplore = stats.onlyExploreCommands;
|
|
102
|
-
|
|
103
|
-
// Score 5: Gate 3-5 approved + >= 3 real tickets active in week
|
|
104
|
-
if (hasGate3to5Approved && ticketsInWeek >= 3) {
|
|
105
|
-
return { depth: 5, depthLabel: 'Sử dụng thường xuyên workflow' };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Score 4: gate.approved >= 1 + >= 1 ticket processed to end
|
|
109
|
-
if (gateApproveds >= 1 && ticketsProcessed >= 1) {
|
|
110
|
-
return { depth: 4, depthLabel: 'Hoàn thành workflow' };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Score 3: gate.start with real ticket + >= 3 sessions
|
|
114
|
-
if (hasGateStartWithTicket && sessions >= 3) {
|
|
115
|
-
return { depth: 3, depthLabel: 'Dùng có workflow' };
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Score 2: has use/prompt/guideline OR >= 2 sessions
|
|
119
|
-
if (hasUseOrPrompt || sessions >= 2) {
|
|
120
|
-
return { depth: 2, depthLabel: 'Bắt đầu dùng' };
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Score 1: only explore commands
|
|
124
|
-
if (hasOnlyExplore) {
|
|
125
|
-
return { depth: 1, depthLabel: 'Mới khám phá' };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return { depth: 1, depthLabel: 'Mới khám phá' };
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function computeImpactScore(stats) {
|
|
132
|
-
// Impact: based on tickets processed and gate depth
|
|
133
|
-
const { ticketsProcessed, gateApproveds, hasGate3to5Approved } = stats;
|
|
134
|
-
if (ticketsProcessed >= 3 && hasGate3to5Approved) return { impact: 5, impactLabel: 'Rất cao' };
|
|
135
|
-
if (ticketsProcessed >= 2 && gateApproveds >= 2) return { impact: 4, impactLabel: 'Cao' };
|
|
136
|
-
if (ticketsProcessed >= 1 && gateApproveds >= 1) return { impact: 3, impactLabel: 'Trung bình' };
|
|
137
|
-
if (stats.gateStarts >= 1) return { impact: 2, impactLabel: 'Thấp' };
|
|
138
|
-
if (stats.totalEvents > 0) return { impact: 1, impactLabel: 'Rất thấp' };
|
|
139
|
-
return { impact: 0, impactLabel: 'Không có' };
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function buildNotes(stats) {
|
|
143
|
-
const parts = [];
|
|
144
|
-
if (stats.hasGate3to5Approved) parts.push('Gate 3-5 approved');
|
|
145
|
-
if (stats.ticketsProcessed > 0) parts.push(`${stats.ticketsProcessed} ticket(s) hoàn thành`);
|
|
146
|
-
if (stats.ticketsInWeek > stats.ticketsProcessed) parts.push(`${stats.ticketsInWeek} ticket(s) trong tuần`);
|
|
147
|
-
if (stats.gateStarts > 0 && stats.ticketsProcessed === 0) parts.push('Có gate.start nhưng chưa hoàn thành ticket');
|
|
148
|
-
if (stats.onlyExploreCommands && stats.totalEvents > 0) parts.push('Chỉ dùng lệnh khám phá');
|
|
149
|
-
if (stats.totalEvents === 0) parts.push('Không có hoạt động');
|
|
150
|
-
return parts.join('; ');
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function analyzeMembers(events) {
|
|
154
|
-
const results = [];
|
|
155
|
-
|
|
156
|
-
for (const email of MEMBERS) {
|
|
157
|
-
const userEvents = events.filter(e => e.user === email);
|
|
158
|
-
const totalEvents = userEvents.length;
|
|
159
|
-
|
|
160
|
-
const activeDaysSet = new Set(userEvents.map(e => e.dateStr));
|
|
161
|
-
const activeDays = activeDaysSet.size;
|
|
162
|
-
|
|
163
|
-
const sessions = detectSessions(userEvents);
|
|
164
|
-
|
|
165
|
-
// Collect unique commands (non-empty, non-dash)
|
|
166
|
-
const commandSet = new Set(
|
|
167
|
-
userEvents.map(e => e.command).filter(c => c && c !== '-')
|
|
168
|
-
);
|
|
169
|
-
const keyCommands = [...commandSet].join(', ');
|
|
170
|
-
|
|
171
|
-
// Gate starts (gate1.start, gate2.start, ...)
|
|
172
|
-
const gateStartEvents = userEvents.filter(e => e.command && e.command.match(/^gate\d+\.start$/));
|
|
173
|
-
const gateStarts = gateStartEvents.length;
|
|
174
|
-
|
|
175
|
-
// Gate approved
|
|
176
|
-
const gateApprovedEvents = userEvents.filter(e => e.command && e.command.match(/^gate\d+\.approved$/));
|
|
177
|
-
const gateApproveds = gateApprovedEvents.length;
|
|
178
|
-
|
|
179
|
-
// Tickets processed to end (reached gate4.approved or gate5.start) - real tickets only
|
|
180
|
-
const completedTicketEvents = userEvents.filter(e =>
|
|
181
|
-
e.command && (e.command === 'gate4.approved' || e.command === 'gate5.start') && isRealTicket(e.ticket)
|
|
182
|
-
);
|
|
183
|
-
const ticketsProcessedSet = new Set(completedTicketEvents.map(e => e.ticket));
|
|
184
|
-
const ticketsProcessed = ticketsProcessedSet.size;
|
|
185
|
-
|
|
186
|
-
// Tickets active in week (any gate event with real ticket)
|
|
187
|
-
const ticketsInWeekSet = new Set(
|
|
188
|
-
userEvents
|
|
189
|
-
.filter(e => e.command && e.command.match(/^gate\d+\.(start|approved)$/) && isRealTicket(e.ticket))
|
|
190
|
-
.map(e => e.ticket)
|
|
191
|
-
);
|
|
192
|
-
const ticketsInWeek = ticketsInWeekSet.size;
|
|
193
|
-
|
|
194
|
-
const hasUseOrPrompt = userEvents.some(e =>
|
|
195
|
-
e.command && (USE_COMMANDS.has(e.command) || e.command.startsWith('guide'))
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
const hasGateStartWithRealTicket = gateStartEvents.some(e => isRealTicket(e.ticket));
|
|
199
|
-
|
|
200
|
-
// Gate 3-5 approved
|
|
201
|
-
const hasGate3to5Approved = userEvents.some(e =>
|
|
202
|
-
e.command && e.command.match(/^gate[345]\.approved$/)
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
const nonExploreCommands = userEvents.filter(e =>
|
|
206
|
-
e.command && e.command !== '-' && !EXPLORE_COMMANDS.has(e.command)
|
|
207
|
-
);
|
|
208
|
-
const onlyExploreCommands = totalEvents > 0 && nonExploreCommands.length === 0;
|
|
209
|
-
|
|
210
|
-
const stats = {
|
|
211
|
-
totalEvents, activeDays, sessions, gateStarts, gateApproveds,
|
|
212
|
-
ticketsProcessed, ticketsInWeek, hasUseOrPrompt, hasGateStartWithTicket: hasGateStartWithRealTicket,
|
|
213
|
-
hasGate3to5Approved, onlyExploreCommands
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
const { depth, depthLabel } = score(stats);
|
|
217
|
-
const { impact, impactLabel } = computeImpactScore(stats);
|
|
218
|
-
const totalScore = depth + impact;
|
|
219
|
-
const notes = buildNotes(stats);
|
|
220
|
-
|
|
221
|
-
results.push({
|
|
222
|
-
email, totalEvents, activeDays, keyCommands,
|
|
223
|
-
gateStarts, gateApproveds, ticketsProcessed,
|
|
224
|
-
depth, depthLabel, impact, impactLabel, totalScore, notes
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return results;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function buildExcel(results) {
|
|
232
|
-
const wb = XLSX.utils.book_new();
|
|
233
|
-
|
|
234
|
-
const headers = [
|
|
235
|
-
'#', 'Email', 'Events', 'Active\nDays', 'Key Commands',
|
|
236
|
-
'Gate Start', 'Gate Approved', 'Tickets Processed',
|
|
237
|
-
'Depth\nScore', 'Depth Label',
|
|
238
|
-
'Impact\nScore', 'Impact Label',
|
|
239
|
-
'Total\nScore', 'Notes'
|
|
240
|
-
];
|
|
241
|
-
|
|
242
|
-
const rows = [headers];
|
|
243
|
-
results.forEach((r, i) => {
|
|
244
|
-
rows.push([
|
|
245
|
-
i + 1,
|
|
246
|
-
r.email,
|
|
247
|
-
r.totalEvents,
|
|
248
|
-
r.activeDays,
|
|
249
|
-
r.keyCommands,
|
|
250
|
-
r.gateStarts,
|
|
251
|
-
r.gateApproveds,
|
|
252
|
-
r.ticketsProcessed,
|
|
253
|
-
r.depth,
|
|
254
|
-
r.depthLabel,
|
|
255
|
-
r.impact,
|
|
256
|
-
r.impactLabel,
|
|
257
|
-
r.totalScore,
|
|
258
|
-
r.notes
|
|
259
|
-
]);
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
const ws = XLSX.utils.aoa_to_sheet(rows);
|
|
263
|
-
|
|
264
|
-
// Column widths
|
|
265
|
-
ws['!cols'] = [
|
|
266
|
-
{ wch: 4 }, // #
|
|
267
|
-
{ wch: 32 }, // Email
|
|
268
|
-
{ wch: 8 }, // Events
|
|
269
|
-
{ wch: 8 }, // Active Days
|
|
270
|
-
{ wch: 50 }, // Key Commands
|
|
271
|
-
{ wch: 10 }, // Gate Start
|
|
272
|
-
{ wch: 13 }, // Gate Approved
|
|
273
|
-
{ wch: 16 }, // Tickets Processed
|
|
274
|
-
{ wch: 8 }, // Depth Score
|
|
275
|
-
{ wch: 28 }, // Depth Label
|
|
276
|
-
{ wch: 8 }, // Impact Score
|
|
277
|
-
{ wch: 14 }, // Impact Label
|
|
278
|
-
{ wch: 8 }, // Total Score
|
|
279
|
-
{ wch: 50 }, // Notes
|
|
280
|
-
];
|
|
281
|
-
|
|
282
|
-
XLSX.utils.book_append_sheet(wb, ws, 'Scoring');
|
|
283
|
-
XLSX.writeFile(wb, OUTPUT_FILE);
|
|
284
|
-
console.log(`✅ Excel saved to: ${OUTPUT_FILE}`);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const events = parseLog();
|
|
288
|
-
console.log(`Parsed ${events.length} events`);
|
|
289
|
-
|
|
290
|
-
const results = analyzeMembers(events);
|
|
291
|
-
buildExcel(results);
|
|
292
|
-
|
|
293
|
-
// Print summary
|
|
294
|
-
console.log('\n=== SCORING SUMMARY ===');
|
|
295
|
-
console.log(
|
|
296
|
-
'#'.padEnd(3),
|
|
297
|
-
'Email'.padEnd(34),
|
|
298
|
-
'Events'.padEnd(7),
|
|
299
|
-
'Days'.padEnd(5),
|
|
300
|
-
'GS'.padEnd(4),
|
|
301
|
-
'GA'.padEnd(4),
|
|
302
|
-
'TP'.padEnd(4),
|
|
303
|
-
'Depth'.padEnd(6),
|
|
304
|
-
'Impact'.padEnd(7),
|
|
305
|
-
'Total'
|
|
306
|
-
);
|
|
307
|
-
results.forEach((r, i) => {
|
|
308
|
-
console.log(
|
|
309
|
-
String(i + 1).padEnd(3),
|
|
310
|
-
r.email.padEnd(34),
|
|
311
|
-
String(r.totalEvents).padEnd(7),
|
|
312
|
-
String(r.activeDays).padEnd(5),
|
|
313
|
-
String(r.gateStarts).padEnd(4),
|
|
314
|
-
String(r.gateApproveds).padEnd(4),
|
|
315
|
-
String(r.ticketsProcessed).padEnd(4),
|
|
316
|
-
`${r.depth}(${r.depthLabel.slice(0, 4)})`.padEnd(6),
|
|
317
|
-
String(r.impact).padEnd(7),
|
|
318
|
-
r.totalScore
|
|
319
|
-
);
|
|
320
|
-
});
|