steersync 0.2.0__tar.gz → 0.3.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.
- {steersync-0.2.0 → steersync-0.3.0}/CHANGELOG.md +46 -0
- {steersync-0.2.0 → steersync-0.3.0}/PKG-INFO +113 -48
- {steersync-0.2.0 → steersync-0.3.0}/README.md +112 -47
- {steersync-0.2.0 → steersync-0.3.0}/pyproject.toml +3 -2
- steersync-0.3.0/scripts/prepare_release.py +228 -0
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/__init__.py +1 -1
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/cli.py +152 -8
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/config.py +98 -2
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/errors.py +6 -0
- steersync-0.3.0/src/steersync/gitlab.py +211 -0
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/gitops.py +64 -7
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/hooks.py +86 -17
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/lint.py +171 -41
- steersync-0.3.0/src/steersync/mcp_server.py +311 -0
- steersync-0.3.0/src/steersync/practices.py +533 -0
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/resources/default_config.yaml +63 -7
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/sync.py +205 -14
- {steersync-0.2.0 → steersync-0.3.0}/steersync.example.yaml +16 -2
- steersync-0.3.0/tests/test_automation.py +493 -0
- {steersync-0.2.0 → steersync-0.3.0}/tests/test_cli.py +14 -5
- {steersync-0.2.0 → steersync-0.3.0}/tests/test_collections.py +21 -9
- {steersync-0.2.0 → steersync-0.3.0}/tests/test_config.py +2 -0
- steersync-0.3.0/tests/test_gitlab.py +190 -0
- {steersync-0.2.0 → steersync-0.3.0}/tests/test_gitops.py +20 -6
- {steersync-0.2.0 → steersync-0.3.0}/tests/test_hardening.py +4 -4
- {steersync-0.2.0 → steersync-0.3.0}/tests/test_lint.py +11 -3
- {steersync-0.2.0 → steersync-0.3.0}/tests/test_mcp_server.py +28 -12
- steersync-0.3.0/tests/test_practices.py +216 -0
- steersync-0.3.0/tests/test_prepare_release.py +160 -0
- {steersync-0.2.0 → steersync-0.3.0}/tests/test_sync.py +16 -4
- steersync-0.2.0/src/steersync/mcp_server.py +0 -171
- {steersync-0.2.0 → steersync-0.3.0}/.gitignore +0 -0
- {steersync-0.2.0 → steersync-0.3.0}/LICENSE +0 -0
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/__main__.py +0 -0
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/locking.py +0 -0
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/logging_utils.py +0 -0
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/paths.py +0 -0
- {steersync-0.2.0 → steersync-0.3.0}/src/steersync/steering.py +0 -0
- {steersync-0.2.0 → steersync-0.3.0}/tests/__init__.py +0 -0
- {steersync-0.2.0 → steersync-0.3.0}/tests/conftest.py +0 -0
- {steersync-0.2.0 → steersync-0.3.0}/tests/test_hooks.py +0 -0
- {steersync-0.2.0 → steersync-0.3.0}/tests/test_steering.py +0 -0
|
@@ -5,6 +5,52 @@ Keep a Changelog and the project uses semantic versioning.
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.3.0] - 2026-10-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Two more collections from the Kiro configuration scopes: `prompts` (`.kiro/prompts` and
|
|
13
|
+
`~/.kiro/prompts`, the saved prompts that become slash commands) and `agents-md` (the
|
|
14
|
+
`AGENTS.md` at the repository root). Legacy `.kiro.hook` files are picked up by the hooks
|
|
15
|
+
collection and flagged.
|
|
16
|
+
- Best practice validation. Every collection profile now reports warnings taken from the Kiro
|
|
17
|
+
documentation (steering file naming and `auto` descriptions, the `globs` key, always on
|
|
18
|
+
context budget, missing foundation files, agent tool wildcards and startup globs, inline
|
|
19
|
+
agent hooks on CLI 3.0, hook timeouts and legacy schemas, skill name and description
|
|
20
|
+
limits, machine specific paths in `mcp.json`, prompt name length) plus the `settings` and
|
|
21
|
+
`prompt` profiles. Warnings never block; thresholds live under `lint.practice_limits` and
|
|
22
|
+
`lint.practices: false` turns them off. The rules are listed in `docs/validation.md`.
|
|
23
|
+
- `steersync init` writes the Kiro `SessionStart` hook and registers the steersync MCP server
|
|
24
|
+
in `.kiro/settings/mcp.json` (other servers kept, an existing entry left alone), so a Kiro
|
|
25
|
+
session in any repository refreshes and loads the configuration without being asked.
|
|
26
|
+
`steersync lint` warns when either piece is missing.
|
|
27
|
+
- GitLab merge requests through the REST API: `steersync mr` and the `raise_merge_request`
|
|
28
|
+
MCP tool open or find the merge request for the proposal branch, `push` confirms or creates
|
|
29
|
+
it and prints the URL when the token is present. The token comes from `GITLAB_TOKEN` (name
|
|
30
|
+
configurable with `gitlab.token_env`); `gitlab.api_url`, `gitlab.project` and
|
|
31
|
+
`gitlab.target_branch` are derived from `remote.url` and `remote.default_branch` unless set.
|
|
32
|
+
- `push` and `status` list other proposal branches of the same project still on the central
|
|
33
|
+
remote and warn about them (`branch.warn_existing`).
|
|
34
|
+
- Offline fallback: when the central repository is unreachable and a cache clone exists,
|
|
35
|
+
`pull` and `status` apply the cached default branch and say so (`sync.offline_fallback`).
|
|
36
|
+
`pull --best-effort` runs with the hook time budget.
|
|
37
|
+
- Automatic releases: merging a pull request to `main` computes the version from a
|
|
38
|
+
`release:*` label, a Conventional Commits title prefix or the Unreleased changelog section,
|
|
39
|
+
commits the changelog and version bump, publishes to PyPI and creates the tag and GitHub
|
|
40
|
+
release in the same run (`scripts/prepare_release.py`).
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Proposal branches are named `kiro-config/{project}/{user}-{hash}`; `{user}` now prefers the
|
|
45
|
+
operating system user name (letters, digits and dashes), then the git email, then the git
|
|
46
|
+
name (`branch.user_from`). The template must keep `{project}` and `{hash}`.
|
|
47
|
+
- MCP tools are named by intent and described for the agent: `pull_kiro_config`,
|
|
48
|
+
`push_kiro_config`, `raise_merge_request`, `diff_kiro_config`, `validate_kiro_config` and
|
|
49
|
+
`list_kiro_collections` replace `pull_steering`, `push_steering`, `diff_steering` and
|
|
50
|
+
`lint_steering`. The rendered `mcp.json` entry forwards the GitLab token variable.
|
|
51
|
+
- `pull` logs the central content's lint errors and a one line count of warnings instead of
|
|
52
|
+
every warning.
|
|
53
|
+
|
|
8
54
|
## [0.2.0] - 2026-10-12
|
|
9
55
|
|
|
10
56
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: steersync
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Sync Kiro steering documents between developer machines and a central git repository.
|
|
5
5
|
Project-URL: Homepage, https://github.com/SCGIS-Wales/steersync
|
|
6
6
|
Project-URL: Repository, https://github.com/SCGIS-Wales/steersync
|
|
@@ -59,7 +59,9 @@ layout; each one is configurable and new ones are added with a single YAML block
|
|
|
59
59
|
| `skills` | `.kiro/skills` | `skills` | `skills/` | everything (`SKILL.md`, `scripts/`, `references/`, `assets/`) | `skill`: `SKILL.md` needs `name` and `description`, secrets in scripts |
|
|
60
60
|
| `agents` | `.kiro/agents` | `agents` | `agents/` | `**/*.json`, `**/*.md` | `agent`: valid JSON with a `name`, flags deprecated `toolsSettings` |
|
|
61
61
|
| `hooks` | `.kiro/hooks` | `hooks` | `hooks/` | `**/*.json` | `hooks`: `version`, `hooks[]` with `name`, `trigger`, `action` |
|
|
62
|
-
| `settings` | `.kiro/settings` | none | `settings/` | `lsp.json`, `mcp.json` | `
|
|
62
|
+
| `settings` | `.kiro/settings` | none | `settings/` | `lsp.json`, `mcp.json` | `settings`: valid JSON, `mcpServers` and `languages` shapes, machine paths |
|
|
63
|
+
| `prompts` | `.kiro/prompts` | `prompts` | `prompts/` | `**/*.md` | `prompt`: markdown plus the 50 character name limit |
|
|
64
|
+
| `agents-md` | `AGENTS.md` at the root | none | `agents-md/` | `AGENTS.md` | `markdown` (kept in the project repository too) |
|
|
63
65
|
|
|
64
66
|
Execute bits on skill scripts survive the round trip, hidden files are never synced, and
|
|
65
67
|
files a collection's patterns do not select are never touched on either side. Limit any
|
|
@@ -67,6 +69,11 @@ command to some collections with `--collection NAME` (repeatable), list them wit
|
|
|
67
69
|
`steersync config collections`, and see [docs/central-layout.md](docs/central-layout.md) for
|
|
68
70
|
a worked example of a full `.kiro/` tree in the central repository.
|
|
69
71
|
|
|
72
|
+
Not synced on purpose: `~/.kiro/powers/` (installed by Kiro from a registry, not authored),
|
|
73
|
+
`~/.kiro/settings/cli.json` and `permissions.yaml` (per user, outside any repository) and
|
|
74
|
+
knowledge bases (declared in agent resources, indexed by Kiro). Every path above comes from
|
|
75
|
+
the Kiro configuration scopes documentation; a new kind of content is one YAML block.
|
|
76
|
+
|
|
70
77
|
## Install
|
|
71
78
|
|
|
72
79
|
Requires Python 3.14 or newer and git 2.18 or newer. Every
|
|
@@ -74,7 +81,7 @@ Requires Python 3.14 or newer and git 2.18 or newer. Every
|
|
|
74
81
|
sdist and a `SHA256SUMS` file, with signed build provenance.
|
|
75
82
|
|
|
76
83
|
```bash
|
|
77
|
-
VERSION=0.
|
|
84
|
+
VERSION=0.3.0
|
|
78
85
|
uv tool install "https://github.com/SCGIS-Wales/steersync/releases/download/v${VERSION}/steersync-${VERSION}-py3-none-any.whl"
|
|
79
86
|
# with the MCP server
|
|
80
87
|
uv tool install "steersync[mcp] @ https://github.com/SCGIS-Wales/steersync/releases/download/v${VERSION}/steersync-${VERSION}-py3-none-any.whl"
|
|
@@ -103,7 +110,9 @@ files that the GitHub release carries.
|
|
|
103
110
|
2. Prepare a project. This adds every synced directory (`.kiro/steering/`, `.kiro/specs/`,
|
|
104
111
|
`.kiro/skills/`, ...) and the lock file to `.git/info/exclude` (so centrally managed
|
|
105
112
|
content is never committed to the project by accident; set `git_exclude: false` on a
|
|
106
|
-
collection to keep committing it)
|
|
113
|
+
collection to keep committing it), installs a chaining pre-push hook, writes the Kiro
|
|
114
|
+
`SessionStart` hook that pulls at every session start and registers the steersync MCP
|
|
115
|
+
server in `.kiro/settings/mcp.json`:
|
|
107
116
|
|
|
108
117
|
```bash
|
|
109
118
|
cd ~/code/my-service
|
|
@@ -113,9 +122,28 @@ files that the GitHub release carries.
|
|
|
113
122
|
|
|
114
123
|
3. Edit anything under `.kiro/`. On your next `git push`, or on demand with
|
|
115
124
|
`steersync push`, every collection is proposed to the central repository as one merge
|
|
116
|
-
request.
|
|
125
|
+
request. With `GITLAB_TOKEN` exported, the merge request URL is printed and
|
|
126
|
+
`steersync mr` opens or finds it at any time.
|
|
127
|
+
|
|
128
|
+
Run `steersync doctor` at any time to see what steersync has resolved, and `steersync lint`
|
|
129
|
+
to validate the configuration against Kiro's rules and best practices.
|
|
117
130
|
|
|
118
|
-
|
|
131
|
+
## Starting Kiro in any repository
|
|
132
|
+
|
|
133
|
+
The goal is that anyone who clones a project and starts Kiro gets the whole configuration
|
|
134
|
+
without asking for it. `steersync init` makes that true for a repository:
|
|
135
|
+
|
|
136
|
+
| Piece | What it does | Written by |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| `.kiro/hooks/steersync.json` | A V3 `SessionStart` hook running `steersync pull --quiet --best-effort`, so the session starts from the latest merged configuration. Bounded git time; never fails the session. | `steersync init` (or `steersync kiro-hook --write`) |
|
|
139
|
+
| `.kiro/settings/mcp.json` | The steersync MCP server, so "pull the kiro config", "push my steering", "raise an MR for the kiro config" or "validate the agents" work in chat. | `steersync init` (merged into an existing file, other servers kept) |
|
|
140
|
+
| `.git/hooks/pre-push` | Proposes `.kiro/` on every push of the project. | `steersync init --hook` |
|
|
141
|
+
|
|
142
|
+
Kiro then loads what it finds: always on steering in every session, `fileMatch`, `manual`
|
|
143
|
+
and `auto` steering on CLI V3, skills on demand, prompts as slash commands, hooks and
|
|
144
|
+
settings. `steersync lint` warns (`autoload-hook-missing`, `autoload-mcp-missing`) when a
|
|
145
|
+
repository lacks either piece. On CLI 2.x, where standalone hooks do not exist, paste
|
|
146
|
+
`steersync kiro-hook --engine v2` into the custom agent's `hooks` block.
|
|
119
147
|
|
|
120
148
|
## Central repository layout
|
|
121
149
|
|
|
@@ -150,7 +178,8 @@ subdirectory; `pull` and `status` print a warning pointing at them until that is
|
|
|
150
178
|
| `steersync push [--dry-run] [--scope global] [--no-lint] [-m MSG] [-c NAME] [--json]` | Propose local collections centrally in one branch. `--from-hook` is used by git hooks and never blocks the push unless strict. |
|
|
151
179
|
| `steersync status [-c NAME] [--json]` | Show adds, updates, deletes and local edits per collection in both directions. |
|
|
152
180
|
| `steersync lint [PATHS] [-c NAME] [--profile P] [--json]` | Validate every collection with its profile, or explicit paths. Exit code 4 on errors. |
|
|
153
|
-
| `steersync
|
|
181
|
+
| `steersync mr [--branch NAME] [--title T] [-c NAME] [--json]` | Open, or find, the GitLab merge request for the pushed proposal branch. Needs the token in `GITLAB_TOKEN` (configurable). |
|
|
182
|
+
| `steersync init [--write-config] [--hook] [--no-exclude] [--no-kiro] [--mcp-command PATH]` | Prepare a repository: excludes, git hook, Kiro session hook and MCP server entry. |
|
|
154
183
|
| `steersync install-hook [--force]` | Install or update the chaining pre-push hook. |
|
|
155
184
|
| `steersync kiro-hook [--engine v2\|v3] [--write]` | Render a Kiro hook that runs `steersync pull --quiet` at session start. |
|
|
156
185
|
| `steersync mcp-config [--remote URL]` | Render an `mcp.json` fragment. |
|
|
@@ -176,6 +205,8 @@ for the packaged defaults.
|
|
|
176
205
|
| `STEERSYNC_GIT_BINARY` | `git.binary` |
|
|
177
206
|
| `STEERSYNC_LOG_LEVEL`, `STEERSYNC_LOG_FORMAT` | `logging.*` |
|
|
178
207
|
| `STEERSYNC_STRICT` | `hook.strict` |
|
|
208
|
+
| `STEERSYNC_GITLAB_API_URL`, `STEERSYNC_GITLAB_PROJECT` | `gitlab.api_url`, `gitlab.project` |
|
|
209
|
+
| `GITLAB_TOKEN` (name set by `gitlab.token_env`) | Token with the `api` scope used only for merge requests |
|
|
179
210
|
| `KIRO_HOME` | Location of `~/.kiro`, as honoured by kiro-cli |
|
|
180
211
|
|
|
181
212
|
Unknown keys, invalid patterns, unsafe paths and malformed push options are rejected at load
|
|
@@ -221,6 +252,8 @@ share a directory on either side.
|
|
|
221
252
|
| A failed push always resets the cache clone to a clean default branch. | automatic |
|
|
222
253
|
| From a pre-push hook each git call is capped and retried at most once, and no failure, including an unexpected bug, blocks the push unless strict mode is on. | `hook.timeout_seconds`, `hook.strict` |
|
|
223
254
|
| Central and local content larger than the limits is refused. | `sync.max_files`, `sync.max_total_bytes` |
|
|
255
|
+
| When the central repository cannot be reached and a cache clone exists, `pull` and `status` apply the cached copy of the default branch and say so (`offline` in JSON output). Permanent errors such as authentication failures still fail, and `push` never uses the cache. | `sync.offline_fallback` |
|
|
256
|
+
| `pull --best-effort` (the Kiro session hook) runs with the hook time budget, so a slow server never stalls a session start. | `hook.timeout_seconds` |
|
|
224
257
|
|
|
225
258
|
## How pull decides what to write
|
|
226
259
|
|
|
@@ -257,7 +290,7 @@ Teams that already use [pre-commit](https://pre-commit.com) can use the publishe
|
|
|
257
290
|
default_install_hook_types: [pre-commit, pre-push]
|
|
258
291
|
repos:
|
|
259
292
|
- repo: https://github.com/SCGIS-Wales/steersync
|
|
260
|
-
rev: v0.
|
|
293
|
+
rev: v0.3.0
|
|
261
294
|
hooks:
|
|
262
295
|
- id: steersync-push
|
|
263
296
|
- id: steersync-lint
|
|
@@ -266,52 +299,84 @@ repos:
|
|
|
266
299
|
The lint hook receives the changed files under `.kiro/` and lints each with the profile of
|
|
267
300
|
the collection it belongs to.
|
|
268
301
|
|
|
269
|
-
## GitLab merge requests
|
|
270
|
-
|
|
271
|
-
The default push options create a merge request against the default branch, remove the source
|
|
272
|
-
branch on merge and set a title. Branch names follow `steering/{project}/{user}-{hash}`, where
|
|
273
|
-
the hash covers every file, so two people proposing identical content share one branch and a
|
|
274
|
-
second identical push is reported as `already-proposed`. Servers that do not advertise push
|
|
275
|
-
options (GitHub, plain bare repositories) make git fail, so steersync retries once without them
|
|
276
|
-
and says so. Push options may use `{default_branch}`, `{project}`, `{user}` and `{hash}`.
|
|
302
|
+
## Proposal branches and GitLab merge requests
|
|
277
303
|
|
|
278
|
-
|
|
304
|
+
Pulls always come from the central repository's default branch (`remote.default_branch`,
|
|
305
|
+
`main` unless configured). Pushes go to a proposal branch named after the project repository
|
|
306
|
+
you are working in:
|
|
279
307
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
`hooks` adds `hook-schema`, `hook-trigger` and `hook-action`, `agent` adds `agent-schema`,
|
|
285
|
-
`agent-name` and `agent-deprecated-key`, and `generic` only enforces the size limit and secret
|
|
286
|
-
scanning, allowing binary assets. New profiles are one function in `steersync.lint.PROFILES`.
|
|
308
|
+
```text
|
|
309
|
+
kiro-config/{project}/{user}-{hash}
|
|
310
|
+
kiro-config/acme/platform/entra-app-registration/dgregor-3f9a1c2e
|
|
311
|
+
```
|
|
287
312
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
313
|
+
`{project}` is the slug of the project's origin URL, `{user}` comes from the sources listed
|
|
314
|
+
in `branch.user_from` (the operating system user name first, reduced to letters, digits and
|
|
315
|
+
dashes, then the local part of the git `user.email`, then the git `user.name`) and `{hash}`
|
|
316
|
+
covers every synced file, so two people proposing identical content share one branch and a
|
|
317
|
+
second identical push is reported as `already-proposed`. The template is configurable; it
|
|
318
|
+
must keep `{project}` and `{hash}`.
|
|
319
|
+
|
|
320
|
+
Before pushing, steersync lists the other proposal branches of the same project still on
|
|
321
|
+
the remote and warns about them, so unmerged work is reviewed before more is added
|
|
322
|
+
(`branch.warn_existing`). `steersync status` prints the same list.
|
|
323
|
+
|
|
324
|
+
The merge request is opened in two ways:
|
|
325
|
+
|
|
326
|
+
1. **Push options.** The default `remote.push_options` ask GitLab to create a merge request
|
|
327
|
+
against the default branch, remove the source branch on merge and set a title. Servers
|
|
328
|
+
that do not advertise push options (GitHub, plain bare repositories) make git fail, so
|
|
329
|
+
steersync retries once without them and says so.
|
|
330
|
+
2. **GitLab API.** When the token variable is set (`GITLAB_TOKEN`, name configurable with
|
|
331
|
+
`gitlab.token_env`), `push` confirms or creates the merge request through the REST API and
|
|
332
|
+
prints its URL, and `steersync mr` (or the `raise_merge_request` MCP tool) does the same on
|
|
333
|
+
demand. The API base URL and project path are derived from `remote.url` and can be set
|
|
334
|
+
with `gitlab.api_url` and `gitlab.project`; `gitlab.target_branch` defaults to
|
|
335
|
+
`remote.default_branch`. The token is read from the environment only, never from a file,
|
|
336
|
+
and never logged.
|
|
337
|
+
|
|
338
|
+
## Validation
|
|
339
|
+
|
|
340
|
+
Each collection runs one lint profile: `steering`, `agent`, `hooks`, `skill`, `settings`,
|
|
341
|
+
`prompt`, `markdown`, `json` or `generic`. **Errors** are things Kiro cannot read (front
|
|
342
|
+
matter after a blank line, an unknown inclusion mode, invalid JSON, a `SKILL.md` without
|
|
343
|
+
`name` and `description`) or secrets; they stop a push. **Warnings** are best practices from
|
|
344
|
+
the Kiro documentation, reported objectively and never blocking: a steering file with a
|
|
345
|
+
numeric prefix, an `auto` description that is not written as a trigger, a `globs` key where
|
|
346
|
+
Kiro reads `fileMatchPattern`, an agent with `tools: ["*"]` or a `file://.kiro/steering/**`
|
|
347
|
+
resource that loads everything at startup, inline agent hooks on CLI 3.0, a hook with a
|
|
348
|
+
15 minute timeout, a skill name that is not kebab case, an `mcp.json` with a home directory
|
|
349
|
+
path, more than 48 KiB of always on steering, missing `product.md`, `tech.md` and
|
|
350
|
+
`structure.md`. Every rule, its severity and the documentation page it comes from are listed
|
|
351
|
+
in [docs/validation.md](docs/validation.md); thresholds live under `lint.practice_limits`
|
|
352
|
+
and `lint.practices: false` turns the warnings off.
|
|
301
353
|
|
|
302
354
|
## Kiro integration
|
|
303
355
|
|
|
304
|
-
**Session hook (V3).** `steersync kiro-hook --write` creates
|
|
305
|
-
`SessionStart` trigger that runs
|
|
306
|
-
`steersync
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
server
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
356
|
+
**Session hook (V3).** `steersync init` (or `steersync kiro-hook --write`) creates
|
|
357
|
+
`.kiro/hooks/steersync.json` with a `SessionStart` trigger that runs
|
|
358
|
+
`steersync pull --quiet --best-effort`. For CLI 2.x agents paste the output of
|
|
359
|
+
`steersync kiro-hook --engine v2` into the agent's `hooks` block. Field reports say Kiro hooks
|
|
360
|
+
fire only in headless runs or with a custom agent active, so treat them as a convenience and
|
|
361
|
+
keep the git hook as the dependable trigger.
|
|
362
|
+
|
|
363
|
+
**MCP server.** `steersync init` registers the server in `.kiro/settings/mcp.json`
|
|
364
|
+
(`steersync mcp-config` prints the fragment). The server tells the agent which tool answers
|
|
365
|
+
which request, so plain sentences work:
|
|
366
|
+
|
|
367
|
+
| Say | Tool |
|
|
368
|
+
|---|---|
|
|
369
|
+
| "pull / sync / refresh the kiro config", "update the steering and skills" | `pull_kiro_config` |
|
|
370
|
+
| "push / propose / share my kiro config", "sync my steering up" | `push_kiro_config` |
|
|
371
|
+
| "raise / open an MR for the kiro config", "merge request link" | `raise_merge_request` |
|
|
372
|
+
| "what changed", "status", "any open proposals for this repo" | `diff_kiro_config` |
|
|
373
|
+
| "validate / lint / check the kiro config, agents, hooks, skills" | `validate_kiro_config` |
|
|
374
|
+
| "what is synced and where" | `list_kiro_collections` |
|
|
375
|
+
|
|
376
|
+
Every tool accepts a `collections` list. Kiro does not pass the shell's `PATH`,
|
|
377
|
+
`SSH_AUTH_SOCK`, proxy variables or `GITLAB_TOKEN` to MCP servers, so the rendered entry
|
|
378
|
+
forwards them explicitly through `env`; use `steersync init --mcp-command /abs/path/uvx`
|
|
379
|
+
if `uvx` is not on Kiro's path.
|
|
315
380
|
|
|
316
381
|
**Custom agents.** On Kiro CLI V3 custom agents inherit steering, skills and `AGENTS.md` by
|
|
317
382
|
default, so list only the always on steering files in `resources` and reference skills with
|
|
@@ -28,7 +28,9 @@ layout; each one is configurable and new ones are added with a single YAML block
|
|
|
28
28
|
| `skills` | `.kiro/skills` | `skills` | `skills/` | everything (`SKILL.md`, `scripts/`, `references/`, `assets/`) | `skill`: `SKILL.md` needs `name` and `description`, secrets in scripts |
|
|
29
29
|
| `agents` | `.kiro/agents` | `agents` | `agents/` | `**/*.json`, `**/*.md` | `agent`: valid JSON with a `name`, flags deprecated `toolsSettings` |
|
|
30
30
|
| `hooks` | `.kiro/hooks` | `hooks` | `hooks/` | `**/*.json` | `hooks`: `version`, `hooks[]` with `name`, `trigger`, `action` |
|
|
31
|
-
| `settings` | `.kiro/settings` | none | `settings/` | `lsp.json`, `mcp.json` | `
|
|
31
|
+
| `settings` | `.kiro/settings` | none | `settings/` | `lsp.json`, `mcp.json` | `settings`: valid JSON, `mcpServers` and `languages` shapes, machine paths |
|
|
32
|
+
| `prompts` | `.kiro/prompts` | `prompts` | `prompts/` | `**/*.md` | `prompt`: markdown plus the 50 character name limit |
|
|
33
|
+
| `agents-md` | `AGENTS.md` at the root | none | `agents-md/` | `AGENTS.md` | `markdown` (kept in the project repository too) |
|
|
32
34
|
|
|
33
35
|
Execute bits on skill scripts survive the round trip, hidden files are never synced, and
|
|
34
36
|
files a collection's patterns do not select are never touched on either side. Limit any
|
|
@@ -36,6 +38,11 @@ command to some collections with `--collection NAME` (repeatable), list them wit
|
|
|
36
38
|
`steersync config collections`, and see [docs/central-layout.md](docs/central-layout.md) for
|
|
37
39
|
a worked example of a full `.kiro/` tree in the central repository.
|
|
38
40
|
|
|
41
|
+
Not synced on purpose: `~/.kiro/powers/` (installed by Kiro from a registry, not authored),
|
|
42
|
+
`~/.kiro/settings/cli.json` and `permissions.yaml` (per user, outside any repository) and
|
|
43
|
+
knowledge bases (declared in agent resources, indexed by Kiro). Every path above comes from
|
|
44
|
+
the Kiro configuration scopes documentation; a new kind of content is one YAML block.
|
|
45
|
+
|
|
39
46
|
## Install
|
|
40
47
|
|
|
41
48
|
Requires Python 3.14 or newer and git 2.18 or newer. Every
|
|
@@ -43,7 +50,7 @@ Requires Python 3.14 or newer and git 2.18 or newer. Every
|
|
|
43
50
|
sdist and a `SHA256SUMS` file, with signed build provenance.
|
|
44
51
|
|
|
45
52
|
```bash
|
|
46
|
-
VERSION=0.
|
|
53
|
+
VERSION=0.3.0
|
|
47
54
|
uv tool install "https://github.com/SCGIS-Wales/steersync/releases/download/v${VERSION}/steersync-${VERSION}-py3-none-any.whl"
|
|
48
55
|
# with the MCP server
|
|
49
56
|
uv tool install "steersync[mcp] @ https://github.com/SCGIS-Wales/steersync/releases/download/v${VERSION}/steersync-${VERSION}-py3-none-any.whl"
|
|
@@ -72,7 +79,9 @@ files that the GitHub release carries.
|
|
|
72
79
|
2. Prepare a project. This adds every synced directory (`.kiro/steering/`, `.kiro/specs/`,
|
|
73
80
|
`.kiro/skills/`, ...) and the lock file to `.git/info/exclude` (so centrally managed
|
|
74
81
|
content is never committed to the project by accident; set `git_exclude: false` on a
|
|
75
|
-
collection to keep committing it)
|
|
82
|
+
collection to keep committing it), installs a chaining pre-push hook, writes the Kiro
|
|
83
|
+
`SessionStart` hook that pulls at every session start and registers the steersync MCP
|
|
84
|
+
server in `.kiro/settings/mcp.json`:
|
|
76
85
|
|
|
77
86
|
```bash
|
|
78
87
|
cd ~/code/my-service
|
|
@@ -82,9 +91,28 @@ files that the GitHub release carries.
|
|
|
82
91
|
|
|
83
92
|
3. Edit anything under `.kiro/`. On your next `git push`, or on demand with
|
|
84
93
|
`steersync push`, every collection is proposed to the central repository as one merge
|
|
85
|
-
request.
|
|
94
|
+
request. With `GITLAB_TOKEN` exported, the merge request URL is printed and
|
|
95
|
+
`steersync mr` opens or finds it at any time.
|
|
96
|
+
|
|
97
|
+
Run `steersync doctor` at any time to see what steersync has resolved, and `steersync lint`
|
|
98
|
+
to validate the configuration against Kiro's rules and best practices.
|
|
86
99
|
|
|
87
|
-
|
|
100
|
+
## Starting Kiro in any repository
|
|
101
|
+
|
|
102
|
+
The goal is that anyone who clones a project and starts Kiro gets the whole configuration
|
|
103
|
+
without asking for it. `steersync init` makes that true for a repository:
|
|
104
|
+
|
|
105
|
+
| Piece | What it does | Written by |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `.kiro/hooks/steersync.json` | A V3 `SessionStart` hook running `steersync pull --quiet --best-effort`, so the session starts from the latest merged configuration. Bounded git time; never fails the session. | `steersync init` (or `steersync kiro-hook --write`) |
|
|
108
|
+
| `.kiro/settings/mcp.json` | The steersync MCP server, so "pull the kiro config", "push my steering", "raise an MR for the kiro config" or "validate the agents" work in chat. | `steersync init` (merged into an existing file, other servers kept) |
|
|
109
|
+
| `.git/hooks/pre-push` | Proposes `.kiro/` on every push of the project. | `steersync init --hook` |
|
|
110
|
+
|
|
111
|
+
Kiro then loads what it finds: always on steering in every session, `fileMatch`, `manual`
|
|
112
|
+
and `auto` steering on CLI V3, skills on demand, prompts as slash commands, hooks and
|
|
113
|
+
settings. `steersync lint` warns (`autoload-hook-missing`, `autoload-mcp-missing`) when a
|
|
114
|
+
repository lacks either piece. On CLI 2.x, where standalone hooks do not exist, paste
|
|
115
|
+
`steersync kiro-hook --engine v2` into the custom agent's `hooks` block.
|
|
88
116
|
|
|
89
117
|
## Central repository layout
|
|
90
118
|
|
|
@@ -119,7 +147,8 @@ subdirectory; `pull` and `status` print a warning pointing at them until that is
|
|
|
119
147
|
| `steersync push [--dry-run] [--scope global] [--no-lint] [-m MSG] [-c NAME] [--json]` | Propose local collections centrally in one branch. `--from-hook` is used by git hooks and never blocks the push unless strict. |
|
|
120
148
|
| `steersync status [-c NAME] [--json]` | Show adds, updates, deletes and local edits per collection in both directions. |
|
|
121
149
|
| `steersync lint [PATHS] [-c NAME] [--profile P] [--json]` | Validate every collection with its profile, or explicit paths. Exit code 4 on errors. |
|
|
122
|
-
| `steersync
|
|
150
|
+
| `steersync mr [--branch NAME] [--title T] [-c NAME] [--json]` | Open, or find, the GitLab merge request for the pushed proposal branch. Needs the token in `GITLAB_TOKEN` (configurable). |
|
|
151
|
+
| `steersync init [--write-config] [--hook] [--no-exclude] [--no-kiro] [--mcp-command PATH]` | Prepare a repository: excludes, git hook, Kiro session hook and MCP server entry. |
|
|
123
152
|
| `steersync install-hook [--force]` | Install or update the chaining pre-push hook. |
|
|
124
153
|
| `steersync kiro-hook [--engine v2\|v3] [--write]` | Render a Kiro hook that runs `steersync pull --quiet` at session start. |
|
|
125
154
|
| `steersync mcp-config [--remote URL]` | Render an `mcp.json` fragment. |
|
|
@@ -145,6 +174,8 @@ for the packaged defaults.
|
|
|
145
174
|
| `STEERSYNC_GIT_BINARY` | `git.binary` |
|
|
146
175
|
| `STEERSYNC_LOG_LEVEL`, `STEERSYNC_LOG_FORMAT` | `logging.*` |
|
|
147
176
|
| `STEERSYNC_STRICT` | `hook.strict` |
|
|
177
|
+
| `STEERSYNC_GITLAB_API_URL`, `STEERSYNC_GITLAB_PROJECT` | `gitlab.api_url`, `gitlab.project` |
|
|
178
|
+
| `GITLAB_TOKEN` (name set by `gitlab.token_env`) | Token with the `api` scope used only for merge requests |
|
|
148
179
|
| `KIRO_HOME` | Location of `~/.kiro`, as honoured by kiro-cli |
|
|
149
180
|
|
|
150
181
|
Unknown keys, invalid patterns, unsafe paths and malformed push options are rejected at load
|
|
@@ -190,6 +221,8 @@ share a directory on either side.
|
|
|
190
221
|
| A failed push always resets the cache clone to a clean default branch. | automatic |
|
|
191
222
|
| From a pre-push hook each git call is capped and retried at most once, and no failure, including an unexpected bug, blocks the push unless strict mode is on. | `hook.timeout_seconds`, `hook.strict` |
|
|
192
223
|
| Central and local content larger than the limits is refused. | `sync.max_files`, `sync.max_total_bytes` |
|
|
224
|
+
| When the central repository cannot be reached and a cache clone exists, `pull` and `status` apply the cached copy of the default branch and say so (`offline` in JSON output). Permanent errors such as authentication failures still fail, and `push` never uses the cache. | `sync.offline_fallback` |
|
|
225
|
+
| `pull --best-effort` (the Kiro session hook) runs with the hook time budget, so a slow server never stalls a session start. | `hook.timeout_seconds` |
|
|
193
226
|
|
|
194
227
|
## How pull decides what to write
|
|
195
228
|
|
|
@@ -226,7 +259,7 @@ Teams that already use [pre-commit](https://pre-commit.com) can use the publishe
|
|
|
226
259
|
default_install_hook_types: [pre-commit, pre-push]
|
|
227
260
|
repos:
|
|
228
261
|
- repo: https://github.com/SCGIS-Wales/steersync
|
|
229
|
-
rev: v0.
|
|
262
|
+
rev: v0.3.0
|
|
230
263
|
hooks:
|
|
231
264
|
- id: steersync-push
|
|
232
265
|
- id: steersync-lint
|
|
@@ -235,52 +268,84 @@ repos:
|
|
|
235
268
|
The lint hook receives the changed files under `.kiro/` and lints each with the profile of
|
|
236
269
|
the collection it belongs to.
|
|
237
270
|
|
|
238
|
-
## GitLab merge requests
|
|
239
|
-
|
|
240
|
-
The default push options create a merge request against the default branch, remove the source
|
|
241
|
-
branch on merge and set a title. Branch names follow `steering/{project}/{user}-{hash}`, where
|
|
242
|
-
the hash covers every file, so two people proposing identical content share one branch and a
|
|
243
|
-
second identical push is reported as `already-proposed`. Servers that do not advertise push
|
|
244
|
-
options (GitHub, plain bare repositories) make git fail, so steersync retries once without them
|
|
245
|
-
and says so. Push options may use `{default_branch}`, `{project}`, `{user}` and `{hash}`.
|
|
271
|
+
## Proposal branches and GitLab merge requests
|
|
246
272
|
|
|
247
|
-
|
|
273
|
+
Pulls always come from the central repository's default branch (`remote.default_branch`,
|
|
274
|
+
`main` unless configured). Pushes go to a proposal branch named after the project repository
|
|
275
|
+
you are working in:
|
|
248
276
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
`hooks` adds `hook-schema`, `hook-trigger` and `hook-action`, `agent` adds `agent-schema`,
|
|
254
|
-
`agent-name` and `agent-deprecated-key`, and `generic` only enforces the size limit and secret
|
|
255
|
-
scanning, allowing binary assets. New profiles are one function in `steersync.lint.PROFILES`.
|
|
277
|
+
```text
|
|
278
|
+
kiro-config/{project}/{user}-{hash}
|
|
279
|
+
kiro-config/acme/platform/entra-app-registration/dgregor-3f9a1c2e
|
|
280
|
+
```
|
|
256
281
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
282
|
+
`{project}` is the slug of the project's origin URL, `{user}` comes from the sources listed
|
|
283
|
+
in `branch.user_from` (the operating system user name first, reduced to letters, digits and
|
|
284
|
+
dashes, then the local part of the git `user.email`, then the git `user.name`) and `{hash}`
|
|
285
|
+
covers every synced file, so two people proposing identical content share one branch and a
|
|
286
|
+
second identical push is reported as `already-proposed`. The template is configurable; it
|
|
287
|
+
must keep `{project}` and `{hash}`.
|
|
288
|
+
|
|
289
|
+
Before pushing, steersync lists the other proposal branches of the same project still on
|
|
290
|
+
the remote and warns about them, so unmerged work is reviewed before more is added
|
|
291
|
+
(`branch.warn_existing`). `steersync status` prints the same list.
|
|
292
|
+
|
|
293
|
+
The merge request is opened in two ways:
|
|
294
|
+
|
|
295
|
+
1. **Push options.** The default `remote.push_options` ask GitLab to create a merge request
|
|
296
|
+
against the default branch, remove the source branch on merge and set a title. Servers
|
|
297
|
+
that do not advertise push options (GitHub, plain bare repositories) make git fail, so
|
|
298
|
+
steersync retries once without them and says so.
|
|
299
|
+
2. **GitLab API.** When the token variable is set (`GITLAB_TOKEN`, name configurable with
|
|
300
|
+
`gitlab.token_env`), `push` confirms or creates the merge request through the REST API and
|
|
301
|
+
prints its URL, and `steersync mr` (or the `raise_merge_request` MCP tool) does the same on
|
|
302
|
+
demand. The API base URL and project path are derived from `remote.url` and can be set
|
|
303
|
+
with `gitlab.api_url` and `gitlab.project`; `gitlab.target_branch` defaults to
|
|
304
|
+
`remote.default_branch`. The token is read from the environment only, never from a file,
|
|
305
|
+
and never logged.
|
|
306
|
+
|
|
307
|
+
## Validation
|
|
308
|
+
|
|
309
|
+
Each collection runs one lint profile: `steering`, `agent`, `hooks`, `skill`, `settings`,
|
|
310
|
+
`prompt`, `markdown`, `json` or `generic`. **Errors** are things Kiro cannot read (front
|
|
311
|
+
matter after a blank line, an unknown inclusion mode, invalid JSON, a `SKILL.md` without
|
|
312
|
+
`name` and `description`) or secrets; they stop a push. **Warnings** are best practices from
|
|
313
|
+
the Kiro documentation, reported objectively and never blocking: a steering file with a
|
|
314
|
+
numeric prefix, an `auto` description that is not written as a trigger, a `globs` key where
|
|
315
|
+
Kiro reads `fileMatchPattern`, an agent with `tools: ["*"]` or a `file://.kiro/steering/**`
|
|
316
|
+
resource that loads everything at startup, inline agent hooks on CLI 3.0, a hook with a
|
|
317
|
+
15 minute timeout, a skill name that is not kebab case, an `mcp.json` with a home directory
|
|
318
|
+
path, more than 48 KiB of always on steering, missing `product.md`, `tech.md` and
|
|
319
|
+
`structure.md`. Every rule, its severity and the documentation page it comes from are listed
|
|
320
|
+
in [docs/validation.md](docs/validation.md); thresholds live under `lint.practice_limits`
|
|
321
|
+
and `lint.practices: false` turns the warnings off.
|
|
270
322
|
|
|
271
323
|
## Kiro integration
|
|
272
324
|
|
|
273
|
-
**Session hook (V3).** `steersync kiro-hook --write` creates
|
|
274
|
-
`SessionStart` trigger that runs
|
|
275
|
-
`steersync
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
server
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
325
|
+
**Session hook (V3).** `steersync init` (or `steersync kiro-hook --write`) creates
|
|
326
|
+
`.kiro/hooks/steersync.json` with a `SessionStart` trigger that runs
|
|
327
|
+
`steersync pull --quiet --best-effort`. For CLI 2.x agents paste the output of
|
|
328
|
+
`steersync kiro-hook --engine v2` into the agent's `hooks` block. Field reports say Kiro hooks
|
|
329
|
+
fire only in headless runs or with a custom agent active, so treat them as a convenience and
|
|
330
|
+
keep the git hook as the dependable trigger.
|
|
331
|
+
|
|
332
|
+
**MCP server.** `steersync init` registers the server in `.kiro/settings/mcp.json`
|
|
333
|
+
(`steersync mcp-config` prints the fragment). The server tells the agent which tool answers
|
|
334
|
+
which request, so plain sentences work:
|
|
335
|
+
|
|
336
|
+
| Say | Tool |
|
|
337
|
+
|---|---|
|
|
338
|
+
| "pull / sync / refresh the kiro config", "update the steering and skills" | `pull_kiro_config` |
|
|
339
|
+
| "push / propose / share my kiro config", "sync my steering up" | `push_kiro_config` |
|
|
340
|
+
| "raise / open an MR for the kiro config", "merge request link" | `raise_merge_request` |
|
|
341
|
+
| "what changed", "status", "any open proposals for this repo" | `diff_kiro_config` |
|
|
342
|
+
| "validate / lint / check the kiro config, agents, hooks, skills" | `validate_kiro_config` |
|
|
343
|
+
| "what is synced and where" | `list_kiro_collections` |
|
|
344
|
+
|
|
345
|
+
Every tool accepts a `collections` list. Kiro does not pass the shell's `PATH`,
|
|
346
|
+
`SSH_AUTH_SOCK`, proxy variables or `GITLAB_TOKEN` to MCP servers, so the rendered entry
|
|
347
|
+
forwards them explicitly through `env`; use `steersync init --mcp-command /abs/path/uvx`
|
|
348
|
+
if `uvx` is not on Kiro's path.
|
|
284
349
|
|
|
285
350
|
**Custom agents.** On Kiro CLI V3 custom agents inherit steering, skills and `AGENTS.md` by
|
|
286
351
|
default, so list only the always on steering files in `resources` and reference skills with
|
|
@@ -62,7 +62,7 @@ path = "src/steersync/__init__.py"
|
|
|
62
62
|
packages = ["src/steersync"]
|
|
63
63
|
|
|
64
64
|
[tool.hatch.build.targets.sdist]
|
|
65
|
-
include = ["src/steersync", "tests", "README.md", "CHANGELOG.md", "LICENSE", "steersync.example.yaml"]
|
|
65
|
+
include = ["src/steersync", "tests", "scripts", "README.md", "CHANGELOG.md", "LICENSE", "steersync.example.yaml"]
|
|
66
66
|
|
|
67
67
|
[tool.ruff]
|
|
68
68
|
target-version = "py314"
|
|
@@ -89,6 +89,7 @@ ignore = [
|
|
|
89
89
|
|
|
90
90
|
[tool.ruff.lint.per-file-ignores]
|
|
91
91
|
"tests/**" = ["S101", "S108", "ANN", "PLR2004", "ARG001", "ARG005", "S106", "S105", "E501", "PLC0415"]
|
|
92
|
+
"scripts/**" = ["T201"]
|
|
92
93
|
|
|
93
94
|
[tool.ruff.lint.isort]
|
|
94
95
|
known-first-party = ["steersync"]
|
|
@@ -101,7 +102,7 @@ python_version = "3.14"
|
|
|
101
102
|
strict = true
|
|
102
103
|
warn_unreachable = true
|
|
103
104
|
show_error_codes = true
|
|
104
|
-
files = ["src", "tests"]
|
|
105
|
+
files = ["src", "tests", "scripts"]
|
|
105
106
|
|
|
106
107
|
[[tool.mypy.overrides]]
|
|
107
108
|
module = ["frontmatter", "frontmatter.*", "pymarkdown.*", "mcp.*"]
|