sefrone-ai 1.0.0__tar.gz
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.
- sefrone_ai-1.0.0/PKG-INFO +52 -0
- sefrone_ai-1.0.0/README.md +31 -0
- sefrone_ai-1.0.0/sefrone_ai/__init__.py +5 -0
- sefrone_ai-1.0.0/sefrone_ai/skills/db-migration-workflow/SKILL.md +89 -0
- sefrone_ai-1.0.0/sefrone_ai/skills/decompile-package-workflow/SKILL.md +104 -0
- sefrone_ai-1.0.0/sefrone_ai/skills/e2e-scenario-flow-workflow/SKILL.md +207 -0
- sefrone_ai-1.0.0/sefrone_ai/skills/personal-info-workflow/SKILL.md +109 -0
- sefrone_ai-1.0.0/sefrone_ai/skills_manager.py +75 -0
- sefrone_ai-1.0.0/sefrone_ai.egg-info/PKG-INFO +52 -0
- sefrone_ai-1.0.0/sefrone_ai.egg-info/SOURCES.txt +12 -0
- sefrone_ai-1.0.0/sefrone_ai.egg-info/dependency_links.txt +1 -0
- sefrone_ai-1.0.0/sefrone_ai.egg-info/top_level.txt +1 -0
- sefrone_ai-1.0.0/setup.cfg +4 -0
- sefrone_ai-1.0.0/setup.py +26 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sefrone_ai
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A Python package to provide AI coding assistant helpers for sefrone projects
|
|
5
|
+
Home-page: https://bitbucket.org/sefrone/sefrone_pypi
|
|
6
|
+
Author: Sefrone
|
|
7
|
+
Author-email: contact@sefrone.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: Other/Proprietary License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.7
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: classifier
|
|
16
|
+
Dynamic: description
|
|
17
|
+
Dynamic: description-content-type
|
|
18
|
+
Dynamic: home-page
|
|
19
|
+
Dynamic: requires-python
|
|
20
|
+
Dynamic: summary
|
|
21
|
+
|
|
22
|
+
# Sefrone AI
|
|
23
|
+
|
|
24
|
+
A Python package to provide AI coding assistant helpers for sefrone projects.
|
|
25
|
+
|
|
26
|
+
It bundles a set of Sefrone workflow skills (`SKILL.md` files) and exports them into a
|
|
27
|
+
target repository, either in Claude Code's layout (`.claude/skills`) or GitHub's layout
|
|
28
|
+
(`.github/skills`).
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install sefrone_ai
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from sefrone_ai import SkillsManager
|
|
40
|
+
|
|
41
|
+
# List the skills bundled with this package
|
|
42
|
+
SkillsManager.list_bundled_skills()
|
|
43
|
+
|
|
44
|
+
# Override skill files for a Claude Code style repo (<target>/.claude/skills/<skill>/SKILL.md)
|
|
45
|
+
SkillsManager.export_claude_skills("path/to/target/repo", overwrite=True)
|
|
46
|
+
|
|
47
|
+
# Override skill files for a GitHub style repo (<target>/.github/skills/<skill>/SKILL.md)
|
|
48
|
+
SkillsManager.export_github_skills("path/to/target/repo", overwrite=True)
|
|
49
|
+
|
|
50
|
+
# target_repo_dir is optional; omitting it exports into the current working directory
|
|
51
|
+
SkillsManager.export_claude_skills(overwrite=True)
|
|
52
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Sefrone AI
|
|
2
|
+
|
|
3
|
+
A Python package to provide AI coding assistant helpers for sefrone projects.
|
|
4
|
+
|
|
5
|
+
It bundles a set of Sefrone workflow skills (`SKILL.md` files) and exports them into a
|
|
6
|
+
target repository, either in Claude Code's layout (`.claude/skills`) or GitHub's layout
|
|
7
|
+
(`.github/skills`).
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install sefrone_ai
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from sefrone_ai import SkillsManager
|
|
19
|
+
|
|
20
|
+
# List the skills bundled with this package
|
|
21
|
+
SkillsManager.list_bundled_skills()
|
|
22
|
+
|
|
23
|
+
# Override skill files for a Claude Code style repo (<target>/.claude/skills/<skill>/SKILL.md)
|
|
24
|
+
SkillsManager.export_claude_skills("path/to/target/repo", overwrite=True)
|
|
25
|
+
|
|
26
|
+
# Override skill files for a GitHub style repo (<target>/.github/skills/<skill>/SKILL.md)
|
|
27
|
+
SkillsManager.export_github_skills("path/to/target/repo", overwrite=True)
|
|
28
|
+
|
|
29
|
+
# target_repo_dir is optional; omitting it exports into the current working directory
|
|
30
|
+
SkillsManager.export_claude_skills(overwrite=True)
|
|
31
|
+
```
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: db-migration-workflow
|
|
3
|
+
description: "Create or update SQL migrations and keep schema bootstrap in sync. Use when: adding columns/tables, dropping columns/indexes, enforcing NOT NULL, backfilling existing data, or updating DbSchema/Schema/init.sql to match migrations."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DB Migration Workflow
|
|
7
|
+
|
|
8
|
+
Use this workflow for PostgreSQL schema changes in a repo that follows the `DbSchema/Migrations` + `DbSchema/Schema/init.sql` bootstrap convention (commonly under a `<Something>.Data/DbSchema/` project).
|
|
9
|
+
|
|
10
|
+
## Locate the Schema Files First
|
|
11
|
+
|
|
12
|
+
Before making any change, find this repo's actual paths — do not assume a project name:
|
|
13
|
+
|
|
14
|
+
- Migrations folder: search for a directory named `Migrations` under a `DbSchema` folder (for example `<DataProject>/DbSchema/Migrations`).
|
|
15
|
+
- Bootstrap file: search for `init.sql` under a `DbSchema/Schema` folder.
|
|
16
|
+
- Post-bootstrap file: search for `after_init.sql` next to `init.sql` (may not exist in every repo — if absent, ask whether procedures/triggers live elsewhere).
|
|
17
|
+
|
|
18
|
+
## Conventions
|
|
19
|
+
|
|
20
|
+
- Create new migration files in the migrations folder found above; never edit/rewrite old migrations.
|
|
21
|
+
- Use timestamped file names with purpose, for example: YYYYMMDDHHMMSS_update_store_table.sql.
|
|
22
|
+
- Keep SQL style consistent with existing files: uppercase SQL keywords, snake_case column names, one logical change block per statement.
|
|
23
|
+
- Keep init.sql for table declarations only.
|
|
24
|
+
- Put procedures, functions, and triggers in after_init.sql (if the repo has one), not in init.sql.
|
|
25
|
+
- On PostgreSQL 15+, prefer named UNIQUE NULLS NOT DISTINCT constraints over split partial unique indexes when the business rule is one logical uniqueness constraint across nullable columns.
|
|
26
|
+
|
|
27
|
+
## Steps
|
|
28
|
+
|
|
29
|
+
1. Identify the target table and current schema state in init.sql and the latest migrations.
|
|
30
|
+
2. Create a new migration (do not rewrite old migrations).
|
|
31
|
+
3. If removing indexed columns, drop related indexes first (prefer IF EXISTS when safe).
|
|
32
|
+
4. For new required columns on existing tables, use safe rollout:
|
|
33
|
+
- Add column nullable first.
|
|
34
|
+
- Backfill existing rows.
|
|
35
|
+
- Set NOT NULL after backfill.
|
|
36
|
+
5. If backfill depends on related data, guard failures with explicit exceptions.
|
|
37
|
+
6. If no rows need backfill, skip update logic safely.
|
|
38
|
+
7. Add/update indexes and foreign keys as needed.
|
|
39
|
+
8. Update init.sql so fresh database bootstrap matches the final table schema.
|
|
40
|
+
9. If the change adds or updates procedures, functions, or triggers, update after_init.sql to keep post-bootstrap database objects in sync.
|
|
41
|
+
|
|
42
|
+
## Safety Patterns
|
|
43
|
+
|
|
44
|
+
- Use DO $$ ... $$ blocks for conditional backfills.
|
|
45
|
+
- Use EXISTS checks before heavy updates.
|
|
46
|
+
- Use deterministic row selection for fallback values (for example ORDER BY created_at, id LIMIT 1).
|
|
47
|
+
- Fail fast when required reference data is missing and nulls would violate constraints.
|
|
48
|
+
|
|
49
|
+
## Common Patterns
|
|
50
|
+
|
|
51
|
+
### Add nullable then enforce not null
|
|
52
|
+
|
|
53
|
+
ALTER TABLE my_table
|
|
54
|
+
ADD COLUMN my_column UUID;
|
|
55
|
+
|
|
56
|
+
UPDATE my_table
|
|
57
|
+
SET my_column = ...
|
|
58
|
+
WHERE my_column IS NULL;
|
|
59
|
+
|
|
60
|
+
ALTER TABLE my_table
|
|
61
|
+
ALTER COLUMN my_column SET NOT NULL;
|
|
62
|
+
|
|
63
|
+
### Drop index before dropping column
|
|
64
|
+
|
|
65
|
+
DROP INDEX IF EXISTS idx_my_table_old_column;
|
|
66
|
+
|
|
67
|
+
ALTER TABLE my_table
|
|
68
|
+
DROP COLUMN old_column;
|
|
69
|
+
|
|
70
|
+
### Named uniqueness across nullable columns on PostgreSQL 15+
|
|
71
|
+
|
|
72
|
+
ALTER TABLE my_table
|
|
73
|
+
ADD CONSTRAINT unique_my_table_scope_value
|
|
74
|
+
UNIQUE NULLS NOT DISTINCT (nullable_scope_id, value_id);
|
|
75
|
+
|
|
76
|
+
Use this when the rule is one logical uniqueness constraint and nullable columns are part of the key.
|
|
77
|
+
|
|
78
|
+
### Trigger and procedure placement
|
|
79
|
+
|
|
80
|
+
Keep table DDL in init.sql.
|
|
81
|
+
|
|
82
|
+
Keep CREATE FUNCTION and CREATE TRIGGER statements in after_init.sql.
|
|
83
|
+
|
|
84
|
+
## Done Criteria
|
|
85
|
+
|
|
86
|
+
- New migration file exists with timestamped name.
|
|
87
|
+
- init.sql reflects the same final table shape.
|
|
88
|
+
- Backfill path is safe for existing production data.
|
|
89
|
+
- NOT NULL is only enforced after data is valid.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: decompile-package-workflow
|
|
3
|
+
description: "Decompile any .NET NuGet package or assembly to inspect source-like code. Use when package behavior is unclear, debugging third-party internals, validating DTO/model mappings, or tracing package logic."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Decompile Package Workflow
|
|
7
|
+
|
|
8
|
+
Use this workflow when you need to inspect code inside any NuGet package or .NET assembly (`.dll` / `.nupkg`) — third-party or internal.
|
|
9
|
+
|
|
10
|
+
## Required Tools
|
|
11
|
+
|
|
12
|
+
- Primary decompiler: `ilspycmd` (installed globally)
|
|
13
|
+
- Fallback IL viewer: `dotnet-ildasm` (installed globally)
|
|
14
|
+
|
|
15
|
+
Verify tools:
|
|
16
|
+
|
|
17
|
+
```powershell
|
|
18
|
+
ilspycmd --version
|
|
19
|
+
dotnet-ildasm --version
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Inputs You Need
|
|
23
|
+
|
|
24
|
+
- Package ID (for example `Newtonsoft.Json`) and version, OR path to a local `.dll`/`.nupkg`.
|
|
25
|
+
- Output folder for decompiled files.
|
|
26
|
+
|
|
27
|
+
## Find Package Files
|
|
28
|
+
|
|
29
|
+
Global NuGet cache path:
|
|
30
|
+
|
|
31
|
+
```powershell
|
|
32
|
+
$pkgRoot = Join-Path $env:USERPROFILE ".nuget/packages"
|
|
33
|
+
$pkgPath = Join-Path $pkgRoot "<package-id-lowercase>/<version>"
|
|
34
|
+
Get-ChildItem $pkgPath -Recurse -Include *.dll
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Notes:
|
|
38
|
+
|
|
39
|
+
- NuGet package folder names are lowercase.
|
|
40
|
+
- Prefer `lib/<tfm>/` assemblies (for example `lib/net8.0/`).
|
|
41
|
+
|
|
42
|
+
## Fast Decompile (Single Assembly)
|
|
43
|
+
|
|
44
|
+
To print decompiled C# to terminal:
|
|
45
|
+
|
|
46
|
+
```powershell
|
|
47
|
+
ilspycmd "<path-to-assembly>.dll"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
To dump C# into a folder as a compilable project:
|
|
51
|
+
|
|
52
|
+
```powershell
|
|
53
|
+
ilspycmd -p --nested-directories -o "<output-folder>" "<path-to-assembly>.dll"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
To decompile one specific type only:
|
|
57
|
+
|
|
58
|
+
```powershell
|
|
59
|
+
ilspycmd -t "Namespace.TypeName" "<path-to-assembly>.dll"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
To list available types before selecting one:
|
|
63
|
+
|
|
64
|
+
```powershell
|
|
65
|
+
ilspycmd -l c "<path-to-assembly>.dll"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Decompile From .nupkg
|
|
69
|
+
|
|
70
|
+
If you have a `.nupkg` file directly:
|
|
71
|
+
|
|
72
|
+
```powershell
|
|
73
|
+
ilspycmd -d -o "<output-folder>" "<path-to-package>.nupkg"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This extracts package assemblies so you can then run `ilspycmd -p` on the extracted `.dll` files.
|
|
77
|
+
|
|
78
|
+
## Fallback Workflow (IL Only)
|
|
79
|
+
|
|
80
|
+
If C# decompilation is not possible, inspect IL:
|
|
81
|
+
|
|
82
|
+
```powershell
|
|
83
|
+
dotnet-ildasm "<path-to-assembly>.dll"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Use this for quick inspection of method signatures and control flow.
|
|
87
|
+
|
|
88
|
+
## Investigation Tips
|
|
89
|
+
|
|
90
|
+
- Decompile referenced dependencies too when method bodies are thin wrappers.
|
|
91
|
+
- Confirm Target Framework Moniker (TFM) and pick the matching assembly (for example `net8.0` vs `netstandard2.0`).
|
|
92
|
+
- Decompiled output can differ from original source formatting and local variable names.
|
|
93
|
+
- Prefer behavioral verification (tests/log tracing) before concluding package behavior.
|
|
94
|
+
|
|
95
|
+
## Safety and Compliance
|
|
96
|
+
|
|
97
|
+
- Decompiled code may include proprietary logic. Keep usage limited to debugging, compatibility, and internal maintenance.
|
|
98
|
+
- Do not publish or redistribute decompiled third-party source.
|
|
99
|
+
|
|
100
|
+
## Done Criteria
|
|
101
|
+
|
|
102
|
+
- Decompiled output exists in an output folder for the target assembly.
|
|
103
|
+
- Relevant type/method has been located and inspected.
|
|
104
|
+
- Findings are documented in task notes with assembly version and TFM used.
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: e2e-scenario-flow-workflow
|
|
3
|
+
description: "Create or update API E2E YAML scenarios using flow-based business journeys (not single-endpoint tests). Use when adding end-to-end coverage in a repo's e2e/Scenarios/<Domain> folders that use the sefrone_api_e2e Python framework, including setup, action, verification, and cleanup across multiple steps and runners."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# E2E Scenario Flow Workflow
|
|
7
|
+
|
|
8
|
+
Use this workflow when writing or updating E2E tests in a repo built on the `sefrone_api_e2e` Python package (`ApiE2ETestsManager` / `E2EScenariosManager`).
|
|
9
|
+
|
|
10
|
+
Goal: build scenario files that validate a business flow end-to-end, not isolated endpoint checks.
|
|
11
|
+
|
|
12
|
+
## Locate This Repo's Setup First
|
|
13
|
+
|
|
14
|
+
- Scenario files are YAML files under `e2e/Scenarios/<Domain>/` — discover the actual domain folder names in this repo (for example `Auth`) instead of assuming any specific one.
|
|
15
|
+
- Find the build/runner script (commonly `build.py`) that imports from `sefrone_api_e2e` and registers `ApiE2ETestsManager`/`E2EScenariosManager` — it tells you exactly which runners are available beyond the default REST runner (an `EmailMockRunner` is common; others like a webhook mock runner may or may not be registered).
|
|
16
|
+
|
|
17
|
+
## Framework Facts
|
|
18
|
+
|
|
19
|
+
- `ApiE2ETestsManager.run_all_tests(...)` loads scenarios in filename sort order within each folder.
|
|
20
|
+
- Tests are fail-fast: if one scenario fails, later scenarios are marked skipped.
|
|
21
|
+
- Values saved in one step are namespaced as `<scenario_file_stem>.<key>` in global store.
|
|
22
|
+
- Cross-scenario references must use `{$stored.<scenario_file_stem>.<key>}`.
|
|
23
|
+
- Default runner is `RestApiRunner` for HTTP and `randomize` steps.
|
|
24
|
+
- Extra runners are registered in the build/runner script and can be selected with `runner:`.
|
|
25
|
+
|
|
26
|
+
## Runner Routing Rules
|
|
27
|
+
|
|
28
|
+
- Do not set `runner:` for normal HTTP steps (`method`, `endpoint`) or `randomize`; default `rest` handles them.
|
|
29
|
+
- Use `runner: email` for `check_email` steps (or rely on auto-detection if no `method` key) — requires an email mock runner to be registered.
|
|
30
|
+
- For any other mock runner (e.g. webhook), use its registered `runner:` name and confirm it's actually wired up in this repo's build/runner script before relying on it.
|
|
31
|
+
- If introducing a new runner-specific step type, ensure the runner is registered and that step payload uniquely matches that runner.
|
|
32
|
+
|
|
33
|
+
## Required Scenario Style
|
|
34
|
+
|
|
35
|
+
Every new scenario should represent one business journey with explicit phases:
|
|
36
|
+
|
|
37
|
+
1. Setup/context
|
|
38
|
+
2. Main action(s)
|
|
39
|
+
3. Side-effect verification (state changes, webhooks, emails, balances, counters)
|
|
40
|
+
4. Negative or boundary checks within the same journey when relevant
|
|
41
|
+
5. Teardown/cleanup if entities were created
|
|
42
|
+
|
|
43
|
+
Avoid "single endpoint smoke" scenarios unless the user explicitly asks for endpoint-only coverage.
|
|
44
|
+
|
|
45
|
+
## Authoring Rules
|
|
46
|
+
|
|
47
|
+
- Always include `name` and `base_url` at scenario level.
|
|
48
|
+
- Prefer deterministic step names that describe intent, not just endpoint names.
|
|
49
|
+
- Use `retry` on steps that depend on eventual consistency or startup latency.
|
|
50
|
+
- Use `save` to capture IDs/tokens/quote IDs and reuse later in the same flow.
|
|
51
|
+
- Add assertions for business outcomes, not only response shape.
|
|
52
|
+
- Use `skip_if` for feature-gated behavior or environment-dependent flows.
|
|
53
|
+
- Keep created data unique using `randomize`.
|
|
54
|
+
- Validate key response structure with `expect.body` types and validate behavior with `assertions`.
|
|
55
|
+
- Prefer cleanup steps (`DELETE`, unlink, disable) so reruns remain stable.
|
|
56
|
+
|
|
57
|
+
## Expression and Template Rules
|
|
58
|
+
|
|
59
|
+
- Env vars: `$var(NAME)`
|
|
60
|
+
- Relative date: `$date(0)`, `$date(+1,end)`, `$date(-1,start)`
|
|
61
|
+
- Stored value: `{$stored.key}` for same scenario, `{$stored.other_scenario.key}` for cross-scenario
|
|
62
|
+
- Template read from response/email/webhook:
|
|
63
|
+
- `{{body.response.id}}`
|
|
64
|
+
- `{{body.response.items[0].id}}`
|
|
65
|
+
- `{{body.response.items.count()}}`
|
|
66
|
+
- Assertion operators supported: `==`, `!=`, `>`, `<`, `>=`, `<=`, `in`, `not in`, `include`
|
|
67
|
+
|
|
68
|
+
## Scenario Design Checklist
|
|
69
|
+
|
|
70
|
+
Before finalizing a scenario, verify:
|
|
71
|
+
|
|
72
|
+
- Flow starts from required auth/context and reaches a real business end state.
|
|
73
|
+
- At least one step saves a value used in a later step.
|
|
74
|
+
- Assertions prove behavior (state transitions, identity consistency, side-effects).
|
|
75
|
+
- Failure-prone asynchronous checks use `retry`.
|
|
76
|
+
- Feature flags are respected through `skip_if` when needed.
|
|
77
|
+
- Data created by scenario is cleaned up or intentionally reusable and uniquely named.
|
|
78
|
+
|
|
79
|
+
## Flow Template (Copy and Adapt)
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
name: "<Domain> - <Business Flow Name>"
|
|
83
|
+
base_url: "http://localhost:$var(API_PUBLIC_PORT)"
|
|
84
|
+
skip_if:
|
|
85
|
+
- "<feature> not in {$stored.0_check_features.enabled_features}"
|
|
86
|
+
|
|
87
|
+
steps:
|
|
88
|
+
- name: "Login admin"
|
|
89
|
+
retry:
|
|
90
|
+
attempts: 10
|
|
91
|
+
delay_seconds: 5
|
|
92
|
+
method: "POST"
|
|
93
|
+
endpoint: "/api/v1/apiAuth/account/$var(AUTH_PROJECT_NAME)/login"
|
|
94
|
+
send_as: "json"
|
|
95
|
+
body:
|
|
96
|
+
device: "e2e_test_device"
|
|
97
|
+
pseudo: "$var(DEFAULT_USERNAME)"
|
|
98
|
+
password: "$var(DEFAULT_PASSWORD)"
|
|
99
|
+
expect:
|
|
100
|
+
status: 200
|
|
101
|
+
body:
|
|
102
|
+
response:
|
|
103
|
+
token: string
|
|
104
|
+
save:
|
|
105
|
+
login_token: "{{body.response.token}}"
|
|
106
|
+
|
|
107
|
+
- name: "Generate unique suffix"
|
|
108
|
+
randomize:
|
|
109
|
+
seed_id: 8
|
|
110
|
+
|
|
111
|
+
- name: "Create entity"
|
|
112
|
+
method: "POST"
|
|
113
|
+
endpoint: "/api/v1/<Entity>/create"
|
|
114
|
+
request_headers:
|
|
115
|
+
- x-api-token: "{$stored.login_token}"
|
|
116
|
+
send_as: "json"
|
|
117
|
+
body:
|
|
118
|
+
name: "E2E {$stored.seed_id}"
|
|
119
|
+
isActive: true
|
|
120
|
+
expect:
|
|
121
|
+
status: 200
|
|
122
|
+
body:
|
|
123
|
+
errorCode: number
|
|
124
|
+
message: string
|
|
125
|
+
|
|
126
|
+
- name: "List entities and save id"
|
|
127
|
+
method: "GET"
|
|
128
|
+
endpoint: "/api/v1/<Entity>/list"
|
|
129
|
+
request_headers:
|
|
130
|
+
- x-api-token: "{$stored.login_token}"
|
|
131
|
+
expect:
|
|
132
|
+
status: 200
|
|
133
|
+
body:
|
|
134
|
+
response:
|
|
135
|
+
- id: string
|
|
136
|
+
isActive: boolean
|
|
137
|
+
save:
|
|
138
|
+
entity_id: "{{body.response[0].id}}"
|
|
139
|
+
assertions:
|
|
140
|
+
- "{{body.response[0].isActive}} == True"
|
|
141
|
+
|
|
142
|
+
- name: "Trigger business action"
|
|
143
|
+
method: "POST"
|
|
144
|
+
endpoint: "/api/v1/<Flow>/execute"
|
|
145
|
+
request_headers:
|
|
146
|
+
- x-api-token: "{$stored.login_token}"
|
|
147
|
+
send_as: "json"
|
|
148
|
+
body:
|
|
149
|
+
entityId: "{$stored.entity_id}"
|
|
150
|
+
expect:
|
|
151
|
+
status: 200
|
|
152
|
+
body:
|
|
153
|
+
response:
|
|
154
|
+
state: string
|
|
155
|
+
assertions:
|
|
156
|
+
- "{{body.response.state}} == 'accepted'"
|
|
157
|
+
|
|
158
|
+
- name: "Verify side effect (optional webhook)"
|
|
159
|
+
runner: webhook
|
|
160
|
+
check_webhook:
|
|
161
|
+
path: "/webhook/<path>"
|
|
162
|
+
secret_key: "<secret>"
|
|
163
|
+
clear_after: true
|
|
164
|
+
retry:
|
|
165
|
+
attempts: 20
|
|
166
|
+
delay_seconds: 2
|
|
167
|
+
assertions:
|
|
168
|
+
- "{{webhook.raw_body}} != ''"
|
|
169
|
+
|
|
170
|
+
- name: "Cleanup entity"
|
|
171
|
+
method: "DELETE"
|
|
172
|
+
endpoint: "/api/v1/<Entity>/delete/{$stored.entity_id}"
|
|
173
|
+
request_headers:
|
|
174
|
+
- x-api-token: "{$stored.login_token}"
|
|
175
|
+
expect:
|
|
176
|
+
status: 200
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Cross-Scenario Strategy
|
|
180
|
+
|
|
181
|
+
Use cross-scenario references only when flow prerequisites are expensive and already covered elsewhere.
|
|
182
|
+
|
|
183
|
+
Example (file stem is whatever this repo actually names its scenario files, e.g. `0_check_features`, `1_normal_auth_flow`):
|
|
184
|
+
|
|
185
|
+
- `{$stored.1_setup_account.login_token}`
|
|
186
|
+
- `{$stored.3_create_entity.entity_id}`
|
|
187
|
+
|
|
188
|
+
When adding a new dependent scenario:
|
|
189
|
+
|
|
190
|
+
- Ensure filename ordering guarantees prerequisite scenario runs first.
|
|
191
|
+
- Reuse stable keys from prerequisite scenario `save` blocks.
|
|
192
|
+
- Add comments at top of file to document dependencies.
|
|
193
|
+
|
|
194
|
+
## What To Avoid
|
|
195
|
+
|
|
196
|
+
- One request + status 200 only tests that do not validate outcome.
|
|
197
|
+
- Repeating setup login in every step instead of reusing saved token.
|
|
198
|
+
- Missing cleanup for created resources when delete endpoint exists.
|
|
199
|
+
- Using brittle assertions against full raw messages when a structured assertion is possible.
|
|
200
|
+
- Mixing unrelated business domains in one scenario file.
|
|
201
|
+
|
|
202
|
+
## Done Criteria
|
|
203
|
+
|
|
204
|
+
- Scenario models a complete business flow from setup to verifiable end state.
|
|
205
|
+
- YAML uses this framework's templating, save, assertion, and skip patterns correctly.
|
|
206
|
+
- Runner usage matches step type (`rest`, `email`, `webhook`).
|
|
207
|
+
- Scenario is rerunnable and deterministic in local and pipeline environments.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: personal-info-workflow
|
|
3
|
+
description: "Implement or modify a PersonalInfo document process flow built on the Sefrone.Api.PersonalInfo NuGet package. Use when: adding a new process type, changing document templates, modifying process-status side effects (emails, downstream entity setup), altering access-blocking rules, or wiring a new ISupportPersonalInfoConfigurator method."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Personal Info Workflow
|
|
7
|
+
|
|
8
|
+
Use this skill whenever work involves the KYC / onboarding document process that lives behind `ISupportPersonalInfoConfigurator` from the `Sefrone.Api.PersonalInfo` NuGet package.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Locate This Repo's Implementation First
|
|
13
|
+
|
|
14
|
+
This interface is implemented exactly once per consuming app, under whatever name that app chose (commonly `<App>AuthConfigurator`). Before changing anything:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
grep -rl "ISupportPersonalInfoConfigurator" --include=*.cs
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The file that has `class ... : ISupportPersonalInfoConfigurator` (or lists it among implemented interfaces) is the **single integration point** — do **not** create a second implementation. It is registered by the framework via DI, typically as part of an `IServiceConfigurator` registration.
|
|
21
|
+
|
|
22
|
+
From there, trace what it delegates to (a process-handling/domain service is the common pattern):
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
ISupportPersonalInfoConfigurator (package: Sefrone.Api.PersonalInfo)
|
|
26
|
+
↑
|
|
27
|
+
<App>AuthConfigurator (implements the interface)
|
|
28
|
+
│ often delegates business logic to
|
|
29
|
+
↓
|
|
30
|
+
I<...>ProcessHandlingService (app-specific service interface, if present)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
If the app doesn't split out a separate service, the configurator class itself contains all the logic below.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## ISupportPersonalInfoConfigurator — Method Map
|
|
38
|
+
|
|
39
|
+
These methods come straight from the package interface (`Sefrone.Api.PersonalInfo/Abstract/ISupportPersonalInfoConfigurator.cs`) — read each implementation in this repo's configurator to learn the app-specific behavior, then update accordingly:
|
|
40
|
+
|
|
41
|
+
### `GetProcessTemplates(projectId)` → `IList<ApiPersonalInfoDocumentProcessTemplate>`
|
|
42
|
+
Returns all enabled process types for this app (e.g. different KYC categories). Find where these templates are built (often a static factory class) to see the existing pattern before adding a new one.
|
|
43
|
+
|
|
44
|
+
### `GetDocumentProcessTemplate(projectId, processType)` → `ApiPersonalInfoDocumentProcessTemplate`
|
|
45
|
+
The interface has a default implementation that delegates to `GetProcessTemplates`. If the configurator overrides this with its own switch/lookup, **that override and `GetProcessTemplates` must stay in sync** — adding a process type to one without the other is a common bug.
|
|
46
|
+
|
|
47
|
+
### `GetWorkspaceRequiredFor(projectId)` → `bool`
|
|
48
|
+
Whether workspaces are required for processes under this project. Often a flat `true`/`false`, but check for per-project-type logic.
|
|
49
|
+
|
|
50
|
+
### `GetCountries(projectId, workspaceId)` → `IList<CountryTemplate>`
|
|
51
|
+
Country restrictions for the process, if any. An empty list means no restriction is enforced.
|
|
52
|
+
|
|
53
|
+
### `GetSearchTextAsync(process)` → `Task<string>`
|
|
54
|
+
Builds the searchable text for a process (commonly assembled from name/display fields read off `process` via `GetFieldValue`).
|
|
55
|
+
|
|
56
|
+
### `AddDocumentProcessMetadataAsync(process)` → `Task<DocumentProcessResponse>`
|
|
57
|
+
Enriches `process.Metadata` with app-specific data (e.g. linked account info). Guard against `process.Id` being `null` for in-progress (unsaved) processes before calling any service that requires an ID.
|
|
58
|
+
|
|
59
|
+
### `EnrichDocumentProcessOwnResponseAsync(process, response)` → `Task<DocumentProcessOwnSummaryResponse>`
|
|
60
|
+
The right place for blocking guards before a user can start a new process (e.g. "too many active entities", "existing entity is inactive"). Look for existing `Blocked` responses with translation keys to follow the established naming convention.
|
|
61
|
+
|
|
62
|
+
### `ProcessChangedAsync(change, process, account)`
|
|
63
|
+
Fires on process status transitions. The package's `ApiPersonalInfoChangeType` constants are: `ProcessStatusPendingSubmit`, `ProcessStatusInReview`, `ProcessStatusApproved`, `ProcessStatusBlocked`. Check what this app's implementation does per type — common side effects are sending a notification email and/or creating/updating a downstream domain entity on approval.
|
|
64
|
+
|
|
65
|
+
### `InfoChangedAsync(change, info, account)`
|
|
66
|
+
Fires on personal info changes (address, phone, etc.), independent of document processes. Often a no-op unless the app needs to react to profile changes.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Document Template Structure
|
|
71
|
+
|
|
72
|
+
Each `ApiPersonalInfoDocumentProcessTemplate` contains an ordered list of `ApiPersonalInfoDocumentTemplate` steps, each either a `DataFields` step (form fields) or a `File` step (upload, with allowed extensions and a max size).
|
|
73
|
+
|
|
74
|
+
**Field value access convention** — `process.GetFieldValue(documentNumber, contentNumber)` reads a specific field by its 1-based position in the template. When adding fields, append rather than reorder — existing code likely reads fields by these fixed positions.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Adding a New Process Type — Checklist
|
|
79
|
+
|
|
80
|
+
1. **Declare the type** — find where this app declares its `ApiPersonalInfoDocumentProcessType` constants (a static class wrapping `Init(...)`) and add the new one there, following the existing naming/source-value pattern.
|
|
81
|
+
|
|
82
|
+
2. **Create the template** — find the factory that builds existing templates and add a new method following the same pattern. Number documents sequentially starting at 1.
|
|
83
|
+
|
|
84
|
+
3. **Register the template** in the configurator:
|
|
85
|
+
- Add it to `GetProcessTemplates`.
|
|
86
|
+
- If `GetDocumentProcessTemplate` is overridden with a manual switch, add a branch there too.
|
|
87
|
+
|
|
88
|
+
4. **Extract fields** — if there's a dedicated extraction helper (mapping process type → field indices), add a case for the new type there.
|
|
89
|
+
|
|
90
|
+
5. **Handle status changes** — if the new type needs different side effects on approval/review/block, update the process-status handler found earlier.
|
|
91
|
+
|
|
92
|
+
6. **Update blocking rules** — if `EnrichDocumentProcessOwnResponseAsync` needs type-specific guards, add them there.
|
|
93
|
+
|
|
94
|
+
7. **Add translation keys** — `*TitleKey` / `*DescriptionKey` / `*LabelKey` strings are looked up via `Sefrone.Localization`. Add corresponding entries wherever this repo keeps its translation files.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Permissions
|
|
99
|
+
|
|
100
|
+
Check how this app maps personal-info permissions to account types — look for the permission constants and the permission map referenced by the configurator (or its DI registration). When adding a process type that needs a separate permission level, follow the same pattern: add the permission constant, then wire it into both the general and account-type-specific permission maps.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Common Pitfalls
|
|
105
|
+
|
|
106
|
+
- If the app's process-handling logic wraps DB work in a unit-of-work/transaction, make sure every code path commits or rethrows — don't return early from a status-change handler with an open transaction.
|
|
107
|
+
- The `GetDocumentProcessTemplate` override (if any) and the `GetProcessTemplates` list **must stay in sync** — the interface default calls `GetProcessTemplates`, so a manual override that forgets a type silently diverges from it.
|
|
108
|
+
- `process.Id` can be `null` for in-progress (unsaved) processes; guard before calling any service that takes a process ID.
|
|
109
|
+
- Process-type and similar enum-like values in this package are usually backed by strings — use the existing `.Value` accessor pattern when constructing `ApiPersonalInfoDocumentProcessType`, not a raw string literal.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SkillsManager:
|
|
6
|
+
"""Exports bundled SKILL.md files to a consumer-specified repo."""
|
|
7
|
+
|
|
8
|
+
# Path to bundled skill resources relative to this file
|
|
9
|
+
_BUNDLED_SKILLS_DIR = Path(__file__).parent / "skills"
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def __export_skills(destination_root, overwrite=False):
|
|
13
|
+
"""
|
|
14
|
+
Copy all bundled skills (each a <skill-name>/SKILL.md pair) to *destination_root*.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
destination_root (str | Path): Directory skills are written under. Created if missing.
|
|
18
|
+
overwrite (bool): If True, existing SKILL.md files are overwritten. Default is False.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
list[str]: Names of skills that were exported.
|
|
22
|
+
"""
|
|
23
|
+
dest_root = Path(destination_root)
|
|
24
|
+
|
|
25
|
+
exported = []
|
|
26
|
+
for skill_dir in sorted(SkillsManager._BUNDLED_SKILLS_DIR.iterdir()):
|
|
27
|
+
skill_file = skill_dir / "SKILL.md"
|
|
28
|
+
if not skill_dir.is_dir() or not skill_file.exists():
|
|
29
|
+
continue
|
|
30
|
+
|
|
31
|
+
target_dir = dest_root / skill_dir.name
|
|
32
|
+
target_file = target_dir / "SKILL.md"
|
|
33
|
+
|
|
34
|
+
if target_file.exists() and not overwrite:
|
|
35
|
+
print(f"[SKIP] {skill_dir.name}/SKILL.md already exists (use overwrite=True to replace)")
|
|
36
|
+
continue
|
|
37
|
+
|
|
38
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
shutil.copy2(skill_file, target_file)
|
|
40
|
+
print(f"[OK ] Exported {skill_dir.name}/SKILL.md -> {target_file}")
|
|
41
|
+
exported.append(skill_dir.name)
|
|
42
|
+
|
|
43
|
+
return exported
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def export_claude_skills(target_repo_dir=None, overwrite=False):
|
|
47
|
+
"""Override skill files for Claude-style repos (<target_repo_dir>/.claude/skills).
|
|
48
|
+
|
|
49
|
+
If target_repo_dir is not provided, defaults to the current working directory.
|
|
50
|
+
"""
|
|
51
|
+
repo_dir = Path(target_repo_dir) if target_repo_dir else Path.cwd()
|
|
52
|
+
return SkillsManager.__export_skills(
|
|
53
|
+
repo_dir / ".claude" / "skills",
|
|
54
|
+
overwrite
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def export_github_skills(target_repo_dir=None, overwrite=False):
|
|
59
|
+
"""Override skill files for GitHub-style repos (<target_repo_dir>/.github/skills).
|
|
60
|
+
|
|
61
|
+
If target_repo_dir is not provided, defaults to the current working directory.
|
|
62
|
+
"""
|
|
63
|
+
repo_dir = Path(target_repo_dir) if target_repo_dir else Path.cwd()
|
|
64
|
+
return SkillsManager.__export_skills(
|
|
65
|
+
repo_dir / ".github" / "skills",
|
|
66
|
+
overwrite
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def list_bundled_skills():
|
|
71
|
+
"""Return a list of bundled skill names."""
|
|
72
|
+
src = SkillsManager._BUNDLED_SKILLS_DIR
|
|
73
|
+
if not src.is_dir():
|
|
74
|
+
return []
|
|
75
|
+
return sorted(p.name for p in src.iterdir() if p.is_dir() and (p / "SKILL.md").exists())
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sefrone_ai
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A Python package to provide AI coding assistant helpers for sefrone projects
|
|
5
|
+
Home-page: https://bitbucket.org/sefrone/sefrone_pypi
|
|
6
|
+
Author: Sefrone
|
|
7
|
+
Author-email: contact@sefrone.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: Other/Proprietary License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.7
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: classifier
|
|
16
|
+
Dynamic: description
|
|
17
|
+
Dynamic: description-content-type
|
|
18
|
+
Dynamic: home-page
|
|
19
|
+
Dynamic: requires-python
|
|
20
|
+
Dynamic: summary
|
|
21
|
+
|
|
22
|
+
# Sefrone AI
|
|
23
|
+
|
|
24
|
+
A Python package to provide AI coding assistant helpers for sefrone projects.
|
|
25
|
+
|
|
26
|
+
It bundles a set of Sefrone workflow skills (`SKILL.md` files) and exports them into a
|
|
27
|
+
target repository, either in Claude Code's layout (`.claude/skills`) or GitHub's layout
|
|
28
|
+
(`.github/skills`).
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install sefrone_ai
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from sefrone_ai import SkillsManager
|
|
40
|
+
|
|
41
|
+
# List the skills bundled with this package
|
|
42
|
+
SkillsManager.list_bundled_skills()
|
|
43
|
+
|
|
44
|
+
# Override skill files for a Claude Code style repo (<target>/.claude/skills/<skill>/SKILL.md)
|
|
45
|
+
SkillsManager.export_claude_skills("path/to/target/repo", overwrite=True)
|
|
46
|
+
|
|
47
|
+
# Override skill files for a GitHub style repo (<target>/.github/skills/<skill>/SKILL.md)
|
|
48
|
+
SkillsManager.export_github_skills("path/to/target/repo", overwrite=True)
|
|
49
|
+
|
|
50
|
+
# target_repo_dir is optional; omitting it exports into the current working directory
|
|
51
|
+
SkillsManager.export_claude_skills(overwrite=True)
|
|
52
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
setup.py
|
|
3
|
+
sefrone_ai/__init__.py
|
|
4
|
+
sefrone_ai/skills_manager.py
|
|
5
|
+
sefrone_ai.egg-info/PKG-INFO
|
|
6
|
+
sefrone_ai.egg-info/SOURCES.txt
|
|
7
|
+
sefrone_ai.egg-info/dependency_links.txt
|
|
8
|
+
sefrone_ai.egg-info/top_level.txt
|
|
9
|
+
sefrone_ai/skills/db-migration-workflow/SKILL.md
|
|
10
|
+
sefrone_ai/skills/decompile-package-workflow/SKILL.md
|
|
11
|
+
sefrone_ai/skills/e2e-scenario-flow-workflow/SKILL.md
|
|
12
|
+
sefrone_ai/skills/personal-info-workflow/SKILL.md
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sefrone_ai
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
with open("README.md", "r", encoding="utf-8") as fh:
|
|
4
|
+
long_description = fh.read()
|
|
5
|
+
|
|
6
|
+
setup(
|
|
7
|
+
name="sefrone_ai",
|
|
8
|
+
version="1.0.0",
|
|
9
|
+
author="Sefrone",
|
|
10
|
+
author_email="contact@sefrone.com",
|
|
11
|
+
description="A Python package to provide AI coding assistant helpers for sefrone projects",
|
|
12
|
+
long_description=long_description,
|
|
13
|
+
long_description_content_type="text/markdown",
|
|
14
|
+
url="https://bitbucket.org/sefrone/sefrone_pypi",
|
|
15
|
+
packages=find_packages(),
|
|
16
|
+
classifiers=[
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"License :: Other/Proprietary License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
],
|
|
21
|
+
python_requires=">=3.7",
|
|
22
|
+
package_data={
|
|
23
|
+
"sefrone_ai": ["skills/*/SKILL.md"]
|
|
24
|
+
},
|
|
25
|
+
include_package_data=True,
|
|
26
|
+
)
|