datasecops-cli 0.4.8__tar.gz → 0.5.1__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.
Files changed (57) hide show
  1. datasecops_cli-0.5.1/.github/workflows/test.yml +28 -0
  2. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/CHANGELOG.md +46 -0
  3. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/PKG-INFO +3 -3
  4. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/README.md +2 -2
  5. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/docs/getting-started.md +3 -3
  6. datasecops_cli-0.5.1/docs/git-operations.md +208 -0
  7. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/docs/mcp-server.md +48 -18
  8. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/mcp-servers.json +1 -1
  9. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/pyproject.toml +1 -1
  10. datasecops_cli-0.5.1/src/datasecops_cli/__init__.py +1 -0
  11. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/config.py +7 -1
  12. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/main.py +21 -5
  13. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/menus/configuration.py +15 -4
  14. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/menus/downloads.py +19 -6
  15. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/menus/git_operations.py +121 -43
  16. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/models/project_config.py +15 -2
  17. datasecops_cli-0.5.1/src/datasecops_cli/services/git_service.py +399 -0
  18. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_mcp/connection.py +11 -0
  19. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_mcp/server.py +38 -5
  20. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/tests/test_models.py +1 -2
  21. datasecops_cli-0.4.8/src/datasecops_cli/__init__.py +0 -1
  22. datasecops_cli-0.4.8/src/datasecops_cli/services/git_service.py +0 -183
  23. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/.github/workflows/auto-tag.yml +0 -0
  24. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/.github/workflows/publish-cli.yml +0 -0
  25. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/.gitignore +0 -0
  26. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/DEVELOPMENT.md +0 -0
  27. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/LICENSE +0 -0
  28. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/docs/legacy.md +0 -0
  29. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/docs/legacy_plan_of_action.md +0 -0
  30. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/setup.ps1 +0 -0
  31. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/setup.sh +0 -0
  32. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/menus/__init__.py +0 -0
  33. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/menus/development.py +0 -0
  34. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/models/__init__.py +0 -0
  35. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/models/git_helpers.py +0 -0
  36. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/services/__init__.py +0 -0
  37. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/services/bootstrap_service.py +0 -0
  38. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/services/dbt_project_generator.py +0 -0
  39. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/services/dbt_runner.py +0 -0
  40. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/services/directory_scaffolder.py +0 -0
  41. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/services/download_service.py +0 -0
  42. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/services/linting_service.py +0 -0
  43. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/services/skill_service.py +0 -0
  44. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/services/snowflake_service.py +0 -0
  45. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/services/upstream_service.py +0 -0
  46. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/utilities/__init__.py +0 -0
  47. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/utilities/display.py +0 -0
  48. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/utilities/file_utils.py +0 -0
  49. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_cli/utilities/yaml_utils.py +0 -0
  50. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_mcp/__init__.py +0 -0
  51. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/src/datasecops_mcp/__main__.py +0 -0
  52. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/tests/__init__.py +0 -0
  53. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/tests/test_config.py +0 -0
  54. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/tests/test_file_utils.py +0 -0
  55. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/tests/test_main.py +0 -0
  56. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/tests/test_version.py +0 -0
  57. {datasecops_cli-0.4.8 → datasecops_cli-0.5.1}/tests/test_yaml_utils.py +0 -0
@@ -0,0 +1,28 @@
1
+ name: Test
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+
7
+ jobs:
8
+ test:
9
+ name: Test (Python ${{ matrix.python-version }})
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ['3.11', '3.12', '3.13']
14
+
15
+ steps:
16
+ - name: Checkout repository
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install package with test dependencies
25
+ run: pip install -e ".[test]"
26
+
27
+ - name: Run tests
28
+ run: pytest --tb=short
@@ -2,6 +2,52 @@
2
2
 
3
3
  All notable changes to the DataSecOps CLI are documented in this file.
4
4
 
5
+ ## [0.5.1] - 2026-06-04
6
+
7
+ ### Added
8
+
9
+ - **MCP server CLI arguments** — `datasecops-mcp` now accepts `--connection-name` and `--app-database` arguments, eliminating the dependency on `.datasecops.yml` being in the working directory. This fixes connection failures when AI tools (Cortex Code, Cursor, etc.) launch the MCP server from a different working directory.
10
+ - **`init_snowflake_service()` function** — new public API in `datasecops_mcp.connection` to pre-initialize the Snowflake service singleton with explicit parameters.
11
+
12
+ ### Changed
13
+
14
+ - **MCP registration passes connection details** — all CLI paths that register the MCP server (setup, configuration menu, downloads menu) now pass `--connection-name` and `--app-database` to the registration command and `mcp.json` args.
15
+ - **`DownloadsMenu` accepts `datasecops_config`** — the downloads menu now receives the `DatasecopsConfig` so it can pass connection details during MCP server registration.
16
+
17
+ ## [0.5.0] - 2026-05-21
18
+
19
+ ### Added
20
+
21
+ - **Work Items MCP tool** — new `get_work_items_config` tool in the MCP server returns the configured ticket system (Azure DevOps, Jira, or GitHub Issues), whether ticket numbers are required, and system-specific connection details. Used by the `new-branch` skill.
22
+ - **WorkItems model** — new `WorkItems` model in `project_config.py` stores ticket system configuration separately from source control.
23
+
24
+ ### Changed
25
+
26
+ - **`ticket_number_required` moved from Source Control to Work Items** — the ticket requirement is now part of the WORK_ITEMS configuration (separate from SOURCE_CONTROL). The `get_branching_rules` MCP tool no longer includes this field; use `get_work_items_config` instead.
27
+ - **Branch format default updated** — default branch format changed from `{branch_type}/{branch_name}` to `{branch_type}/{ticket_name}_{name}` with explicit placeholders for ticket and description.
28
+ - **Branch creation uses `str.replace()` instead of `str.format()`** — supports the new `{ticket_name}` and `{name}` placeholders in branch format without breaking on missing keys.
29
+ - **`validate_branch_name` reads ticket requirement from WORK_ITEMS** — the MCP validation tool now queries the WORK_ITEMS config for `ticket_number_required` instead of SOURCE_CONTROL.
30
+ - **Config class loads WORK_ITEMS** — `Config.load_from_native_app()` now also fetches the WORK_ITEMS configuration.
31
+ - **Git operations menu accepts work_items** — `GitOperationsMenu` now receives `WorkItems` to determine ticket enforcement during branch creation.
32
+
33
+ ## [0.4.9] - 2026-05-21
34
+
35
+ ### Added
36
+
37
+ - **Remove from test** — new git menu option to remove a branch from the test integration branch. Displays squash-merged branches by name and drops the selected commit from test's history via rebase, then force-pushes. The test branch is never deleted.
38
+ - **Branch cleanup** — new option in branch management to delete all local branches (except main, test, and current) and prune stale remote tracking references in one step.
39
+ - **Git operations documentation** — `docs/git-operations.md` documenting the full branch strategy, menu structure, and behaviour of each operation.
40
+ - **Ticket integration plan** — `plans/ticket-integration-plan.md` specifying the design for Azure DevOps, Jira, and GitHub Projects ticket fetching during branch creation.
41
+
42
+ ### Changed
43
+
44
+ - **Squash merge into test rebases onto main first** — prevents drift by rebasing the test branch onto `origin/main` before squash-merging, ensuring test always has main as its base with squash commits layered on top.
45
+ - **Branch creation uses configured `branch_format`** — branch names are now built using the `branch_format` field from the framework's SOURCE_CONTROL config instead of a hardcoded pattern.
46
+ - **Deploy to prod enforced from main only** — production deployments are hardcoded to `force=False` and can only originate from the main branch.
47
+ - **Protected branches** — `main` and `test` cannot be deleted via delete, cleanup, or reset operations.
48
+ - **Stash/restore on branch-switching operations** — squash merge and remove from test now stash uncommitted changes before switching branches and restore them afterward, preventing dirty-tree errors.
49
+ - **Test branch auto-creation** — if neither local nor remote `test` branch exists, it is created from `origin/main` automatically during squash merge or remove operations.
50
+
5
51
  ## [0.4.8] - 2026-05-20
6
52
 
7
53
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datasecops-cli
3
- Version: 0.4.8
3
+ Version: 0.5.1
4
4
  Summary: DataSecOps Framework CLI for Snowflake Native App
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -161,7 +161,7 @@ The package includes an MCP (Model Context Protocol) server that exposes your fr
161
161
  **Cortex Code:**
162
162
 
163
163
  ```bash
164
- cortex mcp add datasecops-framework -- datasecops-mcp
164
+ cortex mcp add datasecops-framework -- datasecops-mcp --connection-name <CONNECTION> --app-database <APP_DB>
165
165
  ```
166
166
 
167
167
  **VS Code / Cursor** — add to `.vscode/mcp.json` or `.cursor/mcp.json`:
@@ -171,7 +171,7 @@ cortex mcp add datasecops-framework -- datasecops-mcp
171
171
  "mcpServers": {
172
172
  "datasecops-framework": {
173
173
  "command": "datasecops-mcp",
174
- "args": []
174
+ "args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
175
175
  }
176
176
  }
177
177
  }
@@ -142,7 +142,7 @@ The package includes an MCP (Model Context Protocol) server that exposes your fr
142
142
  **Cortex Code:**
143
143
 
144
144
  ```bash
145
- cortex mcp add datasecops-framework -- datasecops-mcp
145
+ cortex mcp add datasecops-framework -- datasecops-mcp --connection-name <CONNECTION> --app-database <APP_DB>
146
146
  ```
147
147
 
148
148
  **VS Code / Cursor** — add to `.vscode/mcp.json` or `.cursor/mcp.json`:
@@ -152,7 +152,7 @@ cortex mcp add datasecops-framework -- datasecops-mcp
152
152
  "mcpServers": {
153
153
  "datasecops-framework": {
154
154
  "command": "datasecops-mcp",
155
- "args": []
155
+ "args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
156
156
  }
157
157
  }
158
158
  }
@@ -156,7 +156,7 @@ Create `.vscode/mcp.json` in your project root:
156
156
  "mcpServers": {
157
157
  "datasecops-framework": {
158
158
  "command": "datasecops-mcp",
159
- "args": []
159
+ "args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
160
160
  },
161
161
  "dbt": {
162
162
  "command": "uvx",
@@ -185,7 +185,7 @@ For Azure DevOps instead of GitHub:
185
185
  "mcpServers": {
186
186
  "datasecops-framework": {
187
187
  "command": "datasecops-mcp",
188
- "args": []
188
+ "args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
189
189
  },
190
190
  "dbt": {
191
191
  "command": "uvx",
@@ -219,7 +219,7 @@ Add servers from the command line:
219
219
 
220
220
  ```bash
221
221
  # DataSecOps Framework
222
- cortex mcp add datasecops-framework -- datasecops-mcp
222
+ cortex mcp add datasecops-framework -- datasecops-mcp --connection-name <CONNECTION> --app-database <APP_DB>
223
223
 
224
224
  # dbt
225
225
  cortex mcp add dbt -- uvx dbt-mcp
@@ -0,0 +1,208 @@
1
+ # Git Operations
2
+
3
+ The CLI provides a structured git workflow designed around a branching strategy where feature branches are squash-merged into a `test` branch for integration testing, and only `main` is deployed to production.
4
+
5
+ ## Branch Strategy
6
+
7
+ ```
8
+ main ─────────────────────────────────────── prod
9
+
10
+ ├── feature/ticket_name ──┐
11
+ ├── bugfix/ticket_name ──┤── squash merge ──► test
12
+ └── hotfix/ticket_name ──┘
13
+ ```
14
+
15
+ - `main` is the source of truth and the only branch that deploys to production
16
+ - `test` is an integration branch built on top of main (rebased to prevent drift)
17
+ - Feature/bugfix/hotfix branches are created from main and squash-merged into test
18
+ - Branches can be removed from test without affecting main or other branches on test
19
+
20
+ ## Main Menu
21
+
22
+ | Option | Name | Description |
23
+ |--------|------|-------------|
24
+ | 1 | Branching | Manage branches (create, checkout, switch, delete, prune, cleanup, reset) |
25
+ | 2 | Commit | Stage all changes and commit with a message, then push |
26
+ | 3 | Push | Push current branch to remote |
27
+ | 4 | Pull | Pull latest changes from remote for current branch |
28
+ | 5 | Rebase | Rebase current branch with main |
29
+ | 6 | Deploy | Deploy to an environment branch (e.g. test, prod) |
30
+ | 7 | Squash to test | Squash merge current branch into test |
31
+ | 8 | Remove from test | Remove a branch's changes from test |
32
+
33
+ ---
34
+
35
+ ## Operations Detail
36
+
37
+ ### 1. Branching
38
+
39
+ Submenu for branch management operations.
40
+
41
+ | Option | Name | Description |
42
+ |--------|------|-------------|
43
+ | 1 | New | Create a new branch from main |
44
+ | 2 | Checkout | Checkout a remote branch locally |
45
+ | 3 | Switch | Switch to an existing local branch |
46
+ | 4 | Delete | Delete a local branch (and its remote) |
47
+ | 5 | Prune | Remove stale remote tracking references |
48
+ | 6 | Cleanup | Delete all local branches (except main, test, current) and prune remote |
49
+ | 7 | Reset | Hard reset to main and delete all local branches |
50
+
51
+ #### New Branch
52
+
53
+ Creates a branch using the configured `branch_format` (default: `{branch_type}/{ticket_name}_{name}`):
54
+ - Prompts for branch type (feature, bugfix, hotfix — or configured types from Source Control settings)
55
+ - Prompts for ticket number (required or optional based on Work Items config `ticket_number_required`)
56
+ - Prompts for branch name (description)
57
+ - Builds the full name using `format_map` with placeholders: `{branch_type}`, `{ticket_name}`, `{name}`, `{branch_name}` (legacy)
58
+ - Creates from `origin/main`, checks out, and pushes with upstream tracking
59
+
60
+ > **Tip:** Use the `new-branch` Cortex Code skill instead of the CLI menu for an AI-assisted workflow that also fetches ticket details and creates a plan document.
61
+
62
+ #### Delete Branch
63
+
64
+ - Cannot delete the current branch
65
+ - Cannot delete protected branches (`main`, `test`)
66
+ - Deletes both the local branch and the remote branch
67
+
68
+ #### Cleanup
69
+
70
+ - Deletes all local branches except `main`, `test`, and the current branch
71
+ - Prunes stale remote tracking references
72
+
73
+ #### Reset to Main
74
+
75
+ - Checks out main and hard resets to `origin/main`
76
+ - Deletes all local branches except `main` and `test`
77
+
78
+ ---
79
+
80
+ ### 2. Commit
81
+
82
+ - Detects uncommitted changes (modified and untracked files)
83
+ - Displays a summary of changed files
84
+ - Prompts for a commit message
85
+ - Stages all changes, commits, and pushes to remote
86
+
87
+ ---
88
+
89
+ ### 3. Push
90
+
91
+ Pushes the current branch to its remote counterpart.
92
+
93
+ ---
94
+
95
+ ### 4. Pull
96
+
97
+ Pulls the latest changes from remote for the current branch.
98
+
99
+ ---
100
+
101
+ ### 5. Rebase
102
+
103
+ Submenu for rebase operations.
104
+
105
+ | Option | Name | Description |
106
+ |--------|------|-------------|
107
+ | 1 | Rebase | Standard rebase of current branch onto `origin/main` |
108
+ | 2 | Squash & rebase | Squash all commits into one, then rebase onto main |
109
+ | 3 | Continue | Continue rebase after resolving conflicts |
110
+ | 4 | Abort | Abort an in-progress rebase |
111
+
112
+ #### Standard Rebase
113
+
114
+ Fetches from origin and rebases the current branch onto `origin/main`. If conflicts occur, you can resolve them and use "continue" or "abort".
115
+
116
+ #### Squash & Rebase
117
+
118
+ Soft-resets to the main branch point, creates a single squash commit, then rebases onto `origin/main`. This gives you a clean single commit on top of main.
119
+
120
+ ---
121
+
122
+ ### 6. Deploy
123
+
124
+ Pushes the current branch to a configured deployment branch.
125
+
126
+ **Production rules:**
127
+ - Production deployments can only come from the `main` branch
128
+ - If not on main, the CLI will offer to switch to main first
129
+ - Production pushes are never force-pushed
130
+
131
+ **Non-production environments:**
132
+ - Any branch can be pushed to non-production environments
133
+ - Force-push is used for non-production deployments
134
+
135
+ ---
136
+
137
+ ### 7. Squash to Test
138
+
139
+ Squash merges the current feature branch into the `test` integration branch.
140
+
141
+ **Pre-flight checks (before merge):**
142
+
143
+ 1. **Branch must be up to date with main** — if your branch is behind `origin/main`, the merge is rejected. Rebase with main first.
144
+ 2. **File conflict detection** — checks if any files modified on your branch are also modified by another branch's squash-merge commit currently on test:
145
+ - If the conflicting branch **still exists on remote** → merge is **rejected**. You must resolve with the branch owner or remove that branch from test first.
146
+ - If the conflicting branch **no longer exists** (stale) → the stale commit is **automatically removed** from test before proceeding.
147
+
148
+ **What it does (after pre-flight passes):**
149
+ 1. Stashes any uncommitted changes
150
+ 2. Fetches latest from origin
151
+ 3. Checks out the `test` branch (creates it from `origin/main` if it doesn't exist)
152
+ 4. Pulls latest test
153
+ 5. Rebases test onto `origin/main` to prevent drift from main
154
+ 6. Squash merges the feature branch into test
155
+ 7. Commits with message: `squash merge: {branch_name} into test`
156
+ 8. Force-pushes test (required due to the rebase)
157
+ 9. Checks out the original branch and restores stashed changes
158
+
159
+ **Key behaviours:**
160
+ - The test branch always has `main` as its base — squash commits are layered on top
161
+ - Force-push is used because the rebase rewrites test's history to stay aligned with main
162
+ - The commit message naming convention enables the "remove from test" feature
163
+ - File overlap detection prevents two active branches from silently overwriting each other's changes on test
164
+
165
+ ---
166
+
167
+ ### 8. Remove from Test
168
+
169
+ Removes a previously squash-merged branch from the test branch by dropping its commit from history.
170
+
171
+ **What it does:**
172
+ 1. Fetches latest and lists all squash-merge commits on test (identified by commit message prefix `squash merge:`)
173
+ 2. Displays the branches currently on test by name
174
+ 3. User selects which branch to remove
175
+ 4. Stashes any uncommitted changes
176
+ 5. Checks out test
177
+ 6. Rebases test, dropping the selected commit: `git rebase --onto <commit>^ <commit> test`
178
+ 7. Force-pushes test
179
+ 8. Checks out the original branch and restores stashed changes
180
+
181
+ **Key behaviours:**
182
+ - The commit is completely removed from test's history (not reverted)
183
+ - All other branches on test remain intact
184
+ - The test branch itself is never deleted
185
+ - If the rebase fails (e.g. conflicts), it aborts and returns to the original branch
186
+
187
+ ---
188
+
189
+ ## Protected Branches
190
+
191
+ The following branches are protected and cannot be deleted:
192
+ - `main` — source of truth, deploys to production
193
+ - `test` — integration branch for testing
194
+
195
+ These are protected across all operations: delete, cleanup, and reset.
196
+
197
+ ## Error Handling
198
+
199
+ All operations that switch branches follow a safe pattern:
200
+ 1. Stash uncommitted changes before switching
201
+ 2. Perform the operation
202
+ 3. Switch back to the original branch
203
+ 4. Restore stashed changes
204
+
205
+ If any step fails, the error handler will:
206
+ - Abort any in-progress rebase/revert
207
+ - Switch back to the original branch
208
+ - Restore stashed changes
@@ -13,32 +13,25 @@ pip install datasecops-cli[mcp]
13
13
  Run the server:
14
14
 
15
15
  ```bash
16
- datasecops-mcp
16
+ datasecops-mcp --connection-name <CONNECTION> --app-database <APP_DB>
17
17
  ```
18
18
 
19
19
  The server communicates via stdio transport and is designed to be launched by an MCP client (not run interactively).
20
20
 
21
+ If `--connection-name` and `--app-database` are not provided, the server falls back to reading `.datasecops.yml` from the current working directory.
22
+
21
23
  ## Configuring Your AI Tool
22
24
 
23
25
  ### Cortex Code
24
26
 
25
- Add to your Cortex Code MCP configuration:
26
-
27
- ```json
28
- {
29
- "mcpServers": {
30
- "datasecops-framework": {
31
- "command": "datasecops-mcp",
32
- "args": []
33
- }
34
- }
35
- }
27
+ ```bash
28
+ cortex mcp add datasecops-framework -- datasecops-mcp --connection-name <CONNECTION> --app-database <APP_DB>
36
29
  ```
37
30
 
38
31
  ### Claude Code
39
32
 
40
33
  ```bash
41
- claude mcp add datasecops-framework datasecops-mcp
34
+ claude mcp add datasecops-framework datasecops-mcp -- --connection-name <CONNECTION> --app-database <APP_DB>
42
35
  ```
43
36
 
44
37
  ### Cursor / VS Code
@@ -50,7 +43,7 @@ Add to `.cursor/mcp.json` or your IDE's MCP settings:
50
43
  "mcpServers": {
51
44
  "datasecops-framework": {
52
45
  "command": "datasecops-mcp",
53
- "args": []
46
+ "args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
54
47
  }
55
48
  }
56
49
  }
@@ -58,7 +51,7 @@ Add to `.cursor/mcp.json` or your IDE's MCP settings:
58
51
 
59
52
  ## Prerequisites
60
53
 
61
- The MCP server requires a `.datasecops.yml` file in your project root (the same file used by the `datasecops` CLI):
54
+ The MCP server connects to Snowflake using the connection name specified via CLI arguments (or from `.datasecops.yml`):
62
55
 
63
56
  ```yaml
64
57
  connection_name: my_snowflake_connection
@@ -74,7 +67,8 @@ It uses your Snowflake connection from `~/.snowflake/connections.toml` to authen
74
67
 
75
68
  | Tool | Description |
76
69
  |------|-------------|
77
- | `get_branching_rules` | Branch types, naming conventions, ticket requirements, merge strategies |
70
+ | `get_branching_rules` | Branch types, naming conventions, environment branches, merge strategies |
71
+ | `get_work_items_config` | Work item system (Azure DevOps / Jira / GitHub Issues), ticket requirement, connection details |
78
72
  | `get_linting_rules` | SQLFluff rules (dialect, indentation, enabled rule codes) |
79
73
  | `get_dbt_packages` | Approved packages with pinned versions |
80
74
  | `get_pipeline_config` | CI/CD pipeline YAML templates (GitHub Actions or Azure DevOps) |
@@ -110,6 +104,11 @@ The MCP server works transparently in the background. When you ask your AI assis
110
104
 
111
105
  ### Example Interactions
112
106
 
107
+ **Starting work on a ticket (using the `new-branch` skill):**
108
+ > "Start work on ticket JIRA-456"
109
+
110
+ The AI calls `get_branching_rules` for branch format and types, `get_work_items_config` for the ticket system, fetches the ticket details via the platform MCP server (Azure DevOps / Atlassian / GitHub), creates a branch like `feature/JIRA-456_add-customer-dim`, and saves the ticket details to a plan document.
111
+
113
112
  **Creating a branch:**
114
113
  > "Create a new branch for ticket JIRA-456 to add a customer dimension model"
115
114
 
@@ -166,7 +165,7 @@ For full platform integration (PR creation, CI status, issue tracking), add the
166
165
  "mcpServers": {
167
166
  "datasecops-framework": {
168
167
  "command": "datasecops-mcp",
169
- "args": []
168
+ "args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
170
169
  },
171
170
  "github": {
172
171
  "command": "npx",
@@ -193,7 +192,7 @@ This enables:
193
192
  "mcpServers": {
194
193
  "datasecops-framework": {
195
194
  "command": "datasecops-mcp",
196
- "args": []
195
+ "args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
197
196
  },
198
197
  "azure-devops": {
199
198
  "command": "npx",
@@ -212,8 +211,39 @@ This enables:
212
211
  - Creating pull requests with templates
213
212
  - Checking build pipeline status
214
213
  - Linking to work items
214
+ - Managing and updating work items
215
215
  - Managing boards and sprints
216
216
 
217
+ ### Atlassian (Jira)
218
+
219
+ The Atlassian Rovo MCP Server provides access to Jira, Confluence, and Compass. Authentication is handled via OAuth 2.1 (browser flow) or API token — no local credentials needed.
220
+
221
+ ```json
222
+ {
223
+ "mcpServers": {
224
+ "datasecops-framework": {
225
+ "command": "datasecops-mcp",
226
+ "args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
227
+ },
228
+ "atlassian": {
229
+ "url": "https://mcp.atlassian.com/v1/mcp/authv2"
230
+ }
231
+ }
232
+ }
233
+ ```
234
+
235
+ Or via CLI:
236
+
237
+ ```bash
238
+ cortex mcp add atlassian https://mcp.atlassian.com/v1/mcp/authv2 --transport http
239
+ ```
240
+
241
+ This enables:
242
+ - Searching, creating, and updating Jira issues
243
+ - Reading Confluence pages for context
244
+ - Querying Compass components and dependencies
245
+ - Linking tickets to branches and PRs
246
+
217
247
  ## Combined Workflow Example
218
248
 
219
249
  With both the framework MCP server and GitHub MCP server configured, a developer can say:
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "datasecops-framework": {
4
4
  "command": "datasecops-mcp",
5
- "args": [],
5
+ "args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"],
6
6
  "env": {},
7
7
  "description": "DataSecOps Framework governance rules, branching conventions, linting config, and project profiles from your Snowflake Native App"
8
8
  },
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "datasecops-cli"
7
- version = "0.4.8"
7
+ version = "0.5.1"
8
8
  description = "DataSecOps Framework CLI for Snowflake Native App"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -0,0 +1 @@
1
+ __version__ = "0.5.1"
@@ -2,7 +2,7 @@ from pathlib import Path
2
2
  from typing import Optional
3
3
 
4
4
  from datasecops_cli.models.project_config import (
5
- DatasecopsConfig, ProjectSettings, SourceControl, ProjectProfile, DbtTarget
5
+ DatasecopsConfig, ProjectSettings, SourceControl, WorkItems, ProjectProfile, DbtTarget
6
6
  )
7
7
  from datasecops_cli.utilities.yaml_utils import read_datasecops_config, read_dbt_project
8
8
  from datasecops_cli.utilities.display import error_line, info_line
@@ -15,6 +15,7 @@ class Config:
15
15
  self.datasecops: DatasecopsConfig = DatasecopsConfig()
16
16
  self.project_settings: ProjectSettings = ProjectSettings()
17
17
  self.source_control: SourceControl = SourceControl()
18
+ self.work_items: WorkItems = WorkItems()
18
19
  self.profile: Optional[ProjectProfile] = None
19
20
  self.all_profiles: list[ProjectProfile] = []
20
21
  self.project_dir: Path = Path.cwd()
@@ -77,6 +78,11 @@ class Config:
77
78
  if raw:
78
79
  self.source_control = SourceControl(**{k: v for k, v in raw.items() if k in SourceControl.model_fields})
79
80
 
81
+ # Load work items
82
+ raw = snowflake_service.get_framework_config("WORK_ITEMS")
83
+ if raw:
84
+ self.work_items = WorkItems(**{k: v for k, v in raw.items() if k in WorkItems.model_fields})
85
+
80
86
  # Load project profiles
81
87
  profiles_data = snowflake_service.get_project_profiles()
82
88
  if profiles_data:
@@ -175,11 +175,12 @@ def _run_setup(project_dir: Path):
175
175
  # --- Connect to Snowflake ---
176
176
  info_line("")
177
177
  info_line("Connecting to Snowflake...")
178
- from datasecops_cli.models.project_config import DatasecopsConfig, ProjectSettings, ProjectProfile, SourceControl
178
+ from datasecops_cli.models.project_config import DatasecopsConfig, ProjectSettings, ProjectProfile, SourceControl, WorkItems
179
179
  temp_config = DatasecopsConfig(connection_name=connection_name, app_database=app_database)
180
180
  temp_sf = SnowflakeService(temp_config)
181
181
  project_settings = ProjectSettings()
182
182
  source_control = SourceControl()
183
+ work_items = WorkItems()
183
184
  matched_profile = None
184
185
 
185
186
  try:
@@ -196,6 +197,11 @@ def _run_setup(project_dir: Path):
196
197
  if raw_sc:
197
198
  source_control = SourceControl(**{k: v for k, v in raw_sc.items() if k in SourceControl.model_fields})
198
199
 
200
+ # Load WORK_ITEMS settings (for ticket system and ticket_number_required)
201
+ raw_wi = temp_sf.get_framework_config("WORK_ITEMS")
202
+ if raw_wi:
203
+ work_items = WorkItems(**{k: v for k, v in raw_wi.items() if k in WorkItems.model_fields})
204
+
199
205
  # Get account identifier for MCP URL construction
200
206
  account_identifier = ""
201
207
  try:
@@ -311,11 +317,16 @@ def _run_setup(project_dir: Path):
311
317
  info_line("")
312
318
  info_line("Adding MCP servers to Cortex Code...")
313
319
  try:
314
- subprocess.run(["cortex", "mcp", "add", "datasecops-framework", "--", "datasecops-mcp"],
320
+ subprocess.run(["cortex", "mcp", "add", "datasecops-framework", "--",
321
+ "datasecops-mcp", "--connection-name", connection_name,
322
+ "--app-database", app_database],
315
323
  capture_output=True, text=True)
316
324
  success_line("Added datasecops-framework MCP server")
317
325
  except FileNotFoundError:
318
- warning_line("cortex not found — run manually: cortex mcp add datasecops-framework -- datasecops-mcp")
326
+ warning_line(
327
+ "cortex not found — run manually: cortex mcp add datasecops-framework "
328
+ f"-- datasecops-mcp --connection-name {connection_name} --app-database {app_database}"
329
+ )
319
330
 
320
331
  # Add AI Agent MCP server if enabled
321
332
  if ai_agent_mcp_url:
@@ -366,7 +377,10 @@ def _run_setup(project_dir: Path):
366
377
  mcp_dir = dir_map.get(tool_choice, ".vscode")
367
378
  mcp_path = project_dir / mcp_dir / "mcp.json"
368
379
 
369
- servers = {"datasecops-framework": {"command": "datasecops-mcp", "args": []}}
380
+ servers = {"datasecops-framework": {
381
+ "command": "datasecops-mcp",
382
+ "args": ["--connection-name", connection_name, "--app-database", app_database],
383
+ }}
370
384
 
371
385
  # Add AI Agent MCP server if enabled
372
386
  if ai_agent_mcp_url:
@@ -683,7 +697,8 @@ def _main_menu(config: Config, dbt_runner: DbtRunner, git_service: GitService,
683
697
  elif option == 2 and git_service:
684
698
  git_menu = GitOperationsMenu(
685
699
  git_service, config.source_control,
686
- config.get_deployment_branches(), profile_name
700
+ config.get_deployment_branches(), profile_name,
701
+ work_items=config.work_items
687
702
  )
688
703
  git_menu.show()
689
704
 
@@ -741,6 +756,7 @@ def _main_menu(config: Config, dbt_runner: DbtRunner, git_service: GitService,
741
756
  profile=config.profile,
742
757
  source_control=config.source_control,
743
758
  all_profiles=config.all_profiles,
759
+ datasecops_config=config.datasecops,
744
760
  )
745
761
  dl_menu.show()
746
762
 
@@ -167,13 +167,19 @@ class ConfigurationMenu:
167
167
  info_line("")
168
168
  for server in servers:
169
169
  if server == "datasecops-framework":
170
+ conn = self.datasecops_config.connection_name
171
+ app_db = self.datasecops_config.app_database
170
172
  try:
171
- subprocess.run(["cortex", "mcp", "add", "datasecops-framework",
172
- "--", "datasecops-mcp"],
173
+ subprocess.run(["cortex", "mcp", "add", "datasecops-framework", "--",
174
+ "datasecops-mcp", "--connection-name", conn,
175
+ "--app-database", app_db],
173
176
  capture_output=True, text=True)
174
177
  success_line("Added datasecops-framework MCP server")
175
178
  except FileNotFoundError:
176
- warning_line("cortex not found — run manually: cortex mcp add datasecops-framework -- datasecops-mcp")
179
+ warning_line(
180
+ "cortex not found — run manually: cortex mcp add datasecops-framework "
181
+ f"-- datasecops-mcp --connection-name {conn} --app-database {app_db}"
182
+ )
177
183
 
178
184
  elif server == "GitHub":
179
185
  token = get_input_string("Enter your GitHub PAT (or press Enter to skip): ", allow_empty=True)
@@ -228,7 +234,12 @@ class ConfigurationMenu:
228
234
 
229
235
  for server in servers:
230
236
  if server == "datasecops-framework":
231
- server_config["datasecops-framework"] = {"command": "datasecops-mcp", "args": []}
237
+ conn = self.datasecops_config.connection_name
238
+ app_db = self.datasecops_config.app_database
239
+ server_config["datasecops-framework"] = {
240
+ "command": "datasecops-mcp",
241
+ "args": ["--connection-name", conn, "--app-database", app_db],
242
+ }
232
243
  elif server == "GitHub":
233
244
  server_config["github"] = {
234
245
  "command": "npx",