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.
@@ -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,5 @@
1
+ from .skills_manager import SkillsManager
2
+
3
+ __all__ = [
4
+ "SkillsManager",
5
+ ]
@@ -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
+ sefrone_ai
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+ )