alku 0.5.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.
- alku-0.5.0/.gitignore +11 -0
- alku-0.5.0/CHANGELOG.md +102 -0
- alku-0.5.0/LICENSE +21 -0
- alku-0.5.0/PKG-INFO +92 -0
- alku-0.5.0/README.md +69 -0
- alku-0.5.0/pyproject.toml +55 -0
- alku-0.5.0/src/alku/__init__.py +3 -0
- alku-0.5.0/src/alku/__main__.py +4 -0
- alku-0.5.0/src/alku/agent_configs.py +269 -0
- alku-0.5.0/src/alku/claude.py +144 -0
- alku-0.5.0/src/alku/cli.py +35 -0
- alku-0.5.0/src/alku/codebase_memory.py +51 -0
- alku-0.5.0/src/alku/commands/__init__.py +1 -0
- alku-0.5.0/src/alku/commands/common.py +144 -0
- alku-0.5.0/src/alku/commands/docs.py +244 -0
- alku-0.5.0/src/alku/commands/init.py +67 -0
- alku-0.5.0/src/alku/commands/self.py +45 -0
- alku-0.5.0/src/alku/file_transactions.py +484 -0
- alku-0.5.0/src/alku/gate.py +230 -0
- alku-0.5.0/src/alku/generation/__init__.py +5 -0
- alku-0.5.0/src/alku/generation/diagnostics.py +252 -0
- alku-0.5.0/src/alku/generation/orchestration.py +396 -0
- alku-0.5.0/src/alku/generation/rendering.py +241 -0
- alku-0.5.0/src/alku/generation/transactions.py +588 -0
- alku-0.5.0/src/alku/markdown.py +208 -0
- alku-0.5.0/src/alku/migrations/__init__.py +26 -0
- alku-0.5.0/src/alku/migrations/base.py +58 -0
- alku-0.5.0/src/alku/migrations/registry.py +7 -0
- alku-0.5.0/src/alku/migrations/runner.py +103 -0
- alku-0.5.0/src/alku/model_config.py +212 -0
- alku-0.5.0/src/alku/models.py +125 -0
- alku-0.5.0/src/alku/models.yml +28 -0
- alku-0.5.0/src/alku/paths.py +102 -0
- alku-0.5.0/src/alku/pipeline/__init__.py +25 -0
- alku-0.5.0/src/alku/pipeline/base.py +97 -0
- alku-0.5.0/src/alku/pipeline/context.py +162 -0
- alku-0.5.0/src/alku/pipeline/runner.py +136 -0
- alku-0.5.0/src/alku/pipeline/steps.py +396 -0
- alku-0.5.0/src/alku/proposal/__init__.py +1 -0
- alku-0.5.0/src/alku/proposal/identifiers.py +96 -0
- alku-0.5.0/src/alku/proposal/planning.py +78 -0
- alku-0.5.0/src/alku/proposal/rewriting.py +414 -0
- alku-0.5.0/src/alku/proposal/transaction.py +231 -0
- alku-0.5.0/src/alku/proposal/types.py +65 -0
- alku-0.5.0/src/alku/providers.py +147 -0
- alku-0.5.0/src/alku/queries.py +257 -0
- alku-0.5.0/src/alku/repository.py +345 -0
- alku-0.5.0/src/alku/skill_catalog.py +154 -0
- alku-0.5.0/src/alku/skill_planning.py +203 -0
- alku-0.5.0/src/alku/skill_sync.py +134 -0
- alku-0.5.0/src/alku/skill_types.py +37 -0
- alku-0.5.0/src/alku/skill_writes.py +262 -0
- alku-0.5.0/src/alku/skills/alku-architecture/SKILL.md +70 -0
- alku-0.5.0/src/alku/skills/alku-auto/SKILL.md +58 -0
- alku-0.5.0/src/alku/skills/alku-brainstorm/SKILL.md +24 -0
- alku-0.5.0/src/alku/skills/alku-docs/SKILL.md +20 -0
- alku-0.5.0/src/alku/skills/alku-workflow/SKILL.md +57 -0
- alku-0.5.0/src/alku/skills/alku-workflow/references/develop.md +15 -0
- alku-0.5.0/src/alku/skills/alku-workflow/references/research.md +15 -0
- alku-0.5.0/src/alku/skills/alku-workflow/references/review.md +20 -0
- alku-0.5.0/src/alku/structure.py +150 -0
- alku-0.5.0/src/alku/templates/README.md +76 -0
- alku-0.5.0/src/alku/templates/gate.md +8 -0
- alku-0.5.0/src/alku/tooling.py +425 -0
- alku-0.5.0/src/alku/updates.py +244 -0
- alku-0.5.0/src/alku/validation.py +559 -0
alku-0.5.0/.gitignore
ADDED
alku-0.5.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.5.0 - 2026-09-04
|
|
4
|
+
|
|
5
|
+
- Defined the user-facing skill catalog from the root README as exactly
|
|
6
|
+
`alku-workflow`, `alku-auto`, `alku-brainstorm`, `alku-docs`, and
|
|
7
|
+
`alku-architecture`.
|
|
8
|
+
- Added project-growth brainstorming and separated periodic repository-wide
|
|
9
|
+
documentation and architecture maintenance from per-feature workflow review.
|
|
10
|
+
- Moved research, development, review, feature documentation reconciliation,
|
|
11
|
+
and feature architecture review into managed private references consumed
|
|
12
|
+
only by `alku-workflow`.
|
|
13
|
+
- Extended managed skill validation, installation, refresh, generation, and
|
|
14
|
+
live-agent evaluation routing to include private workflow resources without
|
|
15
|
+
exposing helper skill entrypoints.
|
|
16
|
+
- Made `alku init` install OpenAI and Anthropic skills together, with
|
|
17
|
+
provider-native workflow, research, review, and workload-scaled development
|
|
18
|
+
profiles carrying explicit model and effort settings.
|
|
19
|
+
- Replaced the monolithic initialization function table with inheritable
|
|
20
|
+
pipeline-step classes, interactive optional-feature questions, and resumable
|
|
21
|
+
blocking external-agent handoffs.
|
|
22
|
+
- Added an ordered `alku.migrations` framework that updates already-initialized
|
|
23
|
+
user projects during `alku init` with idempotent, versioned, multi-file
|
|
24
|
+
migrations, strict registry validation, structured outcomes, and
|
|
25
|
+
compensating rollback.
|
|
26
|
+
- Replaced duplicated Claude workflow gates with Claude's native `@AGENTS.md`
|
|
27
|
+
import while preserving authored text outside proven legacy managed blocks.
|
|
28
|
+
|
|
29
|
+
## 0.4.0 - 2026-09-04
|
|
30
|
+
|
|
31
|
+
- Reset the installation baseline to current Alku projects: initialization and
|
|
32
|
+
generation no longer recognize or migrate former gate, generated-index,
|
|
33
|
+
configuration, or unmarked skill formats.
|
|
34
|
+
- Removed deprecated duplicate initialization output fields and the obsolete
|
|
35
|
+
dependency-migration reporting step; recommendations now expose only their
|
|
36
|
+
message, shell-free argument vector, and working directory.
|
|
37
|
+
- Required an explicit record kind for `alku docs list` and `alku docs show`,
|
|
38
|
+
and renamed invalid query payloads from `invalid_features` to
|
|
39
|
+
`invalid_records`.
|
|
40
|
+
- Kept current-sentinel managed-file refresh and transactional proposal-width
|
|
41
|
+
normalization as the extension mechanisms for future format changes.
|
|
42
|
+
|
|
43
|
+
## 0.3.0 - 2026-09-04
|
|
44
|
+
|
|
45
|
+
- Made initialization and bundled-skill synchronization idempotently resumable
|
|
46
|
+
with docs-scaffold and skill-catalog collision preflights plus
|
|
47
|
+
interrupted-file cleanup.
|
|
48
|
+
- Added `alku --version`, a single Hatch version source, verified post-install
|
|
49
|
+
self-updates, explicit artifact exclusions, and security/release guidance.
|
|
50
|
+
- Added reusable large-repository regressions for 100,000 documentation and
|
|
51
|
+
code lines, 1,000 features, and 1,000 proposals.
|
|
52
|
+
- Made dependency-cycle validation iterative, removed the fixed generated-index
|
|
53
|
+
word ceiling, and rejected repository-local feature symlinks.
|
|
54
|
+
- Rejected symlinked initialization roots, preflighted documentation scaffold
|
|
55
|
+
collisions before writes, and normalized malformed PyPI JSON failures.
|
|
56
|
+
- Added conditional agent recommendations for migrating a root
|
|
57
|
+
`requirements.txt` to `pyproject.toml`.
|
|
58
|
+
- Added self-update as the first managed workflow action and require `alku init`
|
|
59
|
+
to be rerun after an update.
|
|
60
|
+
- Added a conditional Ruff/ty workflow step for projects with root
|
|
61
|
+
`pyproject.toml` and removed the `alku-quality` command.
|
|
62
|
+
- Bundled Holvi's autonomous backlog skill as `alku-auto` and install it during
|
|
63
|
+
initialization.
|
|
64
|
+
- Prevented delegated feature owners from recursively treating `alku-auto` as
|
|
65
|
+
their governing workflow or starting another backlog-orchestration loop.
|
|
66
|
+
- Moved the reusable agent procedure into a provider-neutral `alku-workflow`
|
|
67
|
+
skill, reduced managed instruction files to a self-repairing loader gate,
|
|
68
|
+
and made Alku-owned skill checks and upgrades fail-closed, root-anchored, and
|
|
69
|
+
transactional.
|
|
70
|
+
- Added focused `alku-research`, `alku-develop`, and `alku-review` contracts,
|
|
71
|
+
routed them through the managed workflow, and removed the autonomous
|
|
72
|
+
orchestrator's hardcoded feature-owner model.
|
|
73
|
+
- Added conditional read-only architecture review and final documentation
|
|
74
|
+
reconciliation skills, with objective risk triggers, recoverable autonomous
|
|
75
|
+
stage ordering, and deterministic orchestration regressions.
|
|
76
|
+
- Replaced the open-ended documentation tree with strict product, feature, and
|
|
77
|
+
proposal record collections; rejected unknown nodes, generated indexes for
|
|
78
|
+
all three collections, and added kind-aware documentation queries.
|
|
79
|
+
- Added record-kind orientation to `alku docs --help` and a complete
|
|
80
|
+
initialized documentation-tree guide with role-selection advice.
|
|
81
|
+
- Reconciled the guides with the public command behavior, made pre-release
|
|
82
|
+
blockers explicit, and removed duplicated collection and implementation
|
|
83
|
+
contracts.
|
|
84
|
+
- Restricted legacy index migration to files with proven generated ownership
|
|
85
|
+
and reported unreadable documentation nodes without uncaught exceptions.
|
|
86
|
+
- Started proposal sequences at three digits and added transactional,
|
|
87
|
+
collection-wide widening at `1000`, `10000`, and later decimal boundaries,
|
|
88
|
+
with exact structured-reference edits, stale-input refusal, and compensating
|
|
89
|
+
rollback for generated indexes and managed gates.
|
|
90
|
+
|
|
91
|
+
## 0.2.0 - 2026-09-04
|
|
92
|
+
|
|
93
|
+
- Split the CLI into `alku init`, `alku docs …`, and `alku self update`.
|
|
94
|
+
- Replaced project YAML configuration with an authoritative, extensible docs
|
|
95
|
+
structure and automatic `scripts/`/`tools/` discovery.
|
|
96
|
+
- Made initialization non-agentic: it reports a manual command and prompt,
|
|
97
|
+
checks `codebase-memory-mcp`, and mirrors managed instructions into both
|
|
98
|
+
`AGENTS.md` and `CLAUDE.md`.
|
|
99
|
+
- Added safe PyPI version checking and user-tool self-update behavior.
|
|
100
|
+
- Migrated the complete test suite to pytest and pytest-cov with a required
|
|
101
|
+
100% line-coverage gate.
|
|
102
|
+
- Documented user-level installation and PATH setup.
|
alku-0.5.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ragnaruk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
alku-0.5.0/PKG-INFO
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: alku
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Meta-framework for modern autonomous agentic development
|
|
5
|
+
Author-email: Ragnaruk <ima1365@me.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: click<9,>=8.1.8
|
|
19
|
+
Requires-Dist: markdown-it-py<5,>=3
|
|
20
|
+
Requires-Dist: packaging<27,>=24
|
|
21
|
+
Requires-Dist: pyyaml<7,>=6
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# Alku
|
|
25
|
+
|
|
26
|
+
Alku is a meta-framework for modern autonomous agentic development.
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
Alku bundles and installs several skills:
|
|
31
|
+
1. `alku-workflow` — main entrypoint; responsible for a complete plan-to-implementation workflow for a single feature. Automatically added to AGENTS.md.
|
|
32
|
+
2. `alku-auto` — autonomous long-term development; takes all planned features and sequentially launches `alku-workflow` subagents for each one.
|
|
33
|
+
3. `alku-brainstorm` — check current project features and create plans for future project growth; launch when you have no idea what to do with the project.
|
|
34
|
+
4. `alku-docs` — generate, check, and reconcile documentation with code; should be run periodically (every 3 features as a baseline) to avoid mess and drift in documentation.
|
|
35
|
+
5. `alku-architecture` — check high-level project structure; should be run periodically (every 3 features as a baseline) to avoid project structure from becoming a mess.
|
|
36
|
+
|
|
37
|
+
Alku takes completely ownership on the root `docs` directory and uses it for project documentation.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
Until a public release exists, install Alku from a checkout:
|
|
42
|
+
|
|
43
|
+
```console
|
|
44
|
+
uv tool install --force .
|
|
45
|
+
uv tool update-shell
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Restart the shell if `uv tool update-shell` changes `PATH`. After publication,
|
|
49
|
+
`uv tool install alku` will install the packaged release.
|
|
50
|
+
|
|
51
|
+
## Quick start
|
|
52
|
+
|
|
53
|
+
To add Alku to the project run `alku init`, it will:
|
|
54
|
+
|
|
55
|
+
1. Run applicable versioned migrations for an existing Alku project;
|
|
56
|
+
2. Create the `docs` directory;
|
|
57
|
+
3. Update the managed `AGENTS.md` gate and make `CLAUDE.md` import it;
|
|
58
|
+
4. Install bundled skills for OpenAI and Claude;
|
|
59
|
+
5. Install provider-native named agent profiles with role-specific effort;
|
|
60
|
+
6. Ask about optional integrations and external-agent handoffs when interactive;
|
|
61
|
+
|
|
62
|
+
After that, launch a new agent session and you're done.
|
|
63
|
+
|
|
64
|
+
## Documentation
|
|
65
|
+
|
|
66
|
+
- [Documentation index](docs/README.md)
|
|
67
|
+
- [Command reference](docs/product/api/README.md)
|
|
68
|
+
- [Architecture](docs/product/architecture/README.md)
|
|
69
|
+
- [Operations](docs/product/operations/README.md)
|
|
70
|
+
- [Development](docs/product/development/README.md)
|
|
71
|
+
- [Release readiness](docs/product/release/README.md)
|
|
72
|
+
|
|
73
|
+
Alku manages product, feature, and proposal records beneath `docs/`. The
|
|
74
|
+
[collection model](docs/features/structured_document_collections/README.md)
|
|
75
|
+
defines the record kinds, and the
|
|
76
|
+
[validation contract](docs/features/repository_validation/design.md) defines
|
|
77
|
+
their allowed structure and integrity rules.
|
|
78
|
+
|
|
79
|
+
## Development
|
|
80
|
+
|
|
81
|
+
```console
|
|
82
|
+
uv sync
|
|
83
|
+
uv run pytest
|
|
84
|
+
uv run ruff check .
|
|
85
|
+
uv run ruff format --check .
|
|
86
|
+
uv run ty check --extra-search-path src .
|
|
87
|
+
uv build
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The test configuration requires 100% line coverage. See the
|
|
91
|
+
[development guide](docs/product/development/README.md) for the change workflow
|
|
92
|
+
and explicit large-repository regression command.
|
alku-0.5.0/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Alku
|
|
2
|
+
|
|
3
|
+
Alku is a meta-framework for modern autonomous agentic development.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
Alku bundles and installs several skills:
|
|
8
|
+
1. `alku-workflow` — main entrypoint; responsible for a complete plan-to-implementation workflow for a single feature. Automatically added to AGENTS.md.
|
|
9
|
+
2. `alku-auto` — autonomous long-term development; takes all planned features and sequentially launches `alku-workflow` subagents for each one.
|
|
10
|
+
3. `alku-brainstorm` — check current project features and create plans for future project growth; launch when you have no idea what to do with the project.
|
|
11
|
+
4. `alku-docs` — generate, check, and reconcile documentation with code; should be run periodically (every 3 features as a baseline) to avoid mess and drift in documentation.
|
|
12
|
+
5. `alku-architecture` — check high-level project structure; should be run periodically (every 3 features as a baseline) to avoid project structure from becoming a mess.
|
|
13
|
+
|
|
14
|
+
Alku takes completely ownership on the root `docs` directory and uses it for project documentation.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
Until a public release exists, install Alku from a checkout:
|
|
19
|
+
|
|
20
|
+
```console
|
|
21
|
+
uv tool install --force .
|
|
22
|
+
uv tool update-shell
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Restart the shell if `uv tool update-shell` changes `PATH`. After publication,
|
|
26
|
+
`uv tool install alku` will install the packaged release.
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
To add Alku to the project run `alku init`, it will:
|
|
31
|
+
|
|
32
|
+
1. Run applicable versioned migrations for an existing Alku project;
|
|
33
|
+
2. Create the `docs` directory;
|
|
34
|
+
3. Update the managed `AGENTS.md` gate and make `CLAUDE.md` import it;
|
|
35
|
+
4. Install bundled skills for OpenAI and Claude;
|
|
36
|
+
5. Install provider-native named agent profiles with role-specific effort;
|
|
37
|
+
6. Ask about optional integrations and external-agent handoffs when interactive;
|
|
38
|
+
|
|
39
|
+
After that, launch a new agent session and you're done.
|
|
40
|
+
|
|
41
|
+
## Documentation
|
|
42
|
+
|
|
43
|
+
- [Documentation index](docs/README.md)
|
|
44
|
+
- [Command reference](docs/product/api/README.md)
|
|
45
|
+
- [Architecture](docs/product/architecture/README.md)
|
|
46
|
+
- [Operations](docs/product/operations/README.md)
|
|
47
|
+
- [Development](docs/product/development/README.md)
|
|
48
|
+
- [Release readiness](docs/product/release/README.md)
|
|
49
|
+
|
|
50
|
+
Alku manages product, feature, and proposal records beneath `docs/`. The
|
|
51
|
+
[collection model](docs/features/structured_document_collections/README.md)
|
|
52
|
+
defines the record kinds, and the
|
|
53
|
+
[validation contract](docs/features/repository_validation/design.md) defines
|
|
54
|
+
their allowed structure and integrity rules.
|
|
55
|
+
|
|
56
|
+
## Development
|
|
57
|
+
|
|
58
|
+
```console
|
|
59
|
+
uv sync
|
|
60
|
+
uv run pytest
|
|
61
|
+
uv run ruff check .
|
|
62
|
+
uv run ruff format --check .
|
|
63
|
+
uv run ty check --extra-search-path src .
|
|
64
|
+
uv build
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The test configuration requires 100% line coverage. See the
|
|
68
|
+
[development guide](docs/product/development/README.md) for the change workflow
|
|
69
|
+
and explicit large-repository regression command.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "alku"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Meta-framework for modern autonomous agentic development"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{ name = "Ragnaruk", email = "ima1365@me.com" }]
|
|
13
|
+
requires-python = ">=3.11"
|
|
14
|
+
dependencies = ["click>=8.1.8,<9", "markdown-it-py>=3,<5", "packaging>=24,<27", "PyYAML>=6,<7"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.scripts]
|
|
28
|
+
alku = "alku.cli:cli"
|
|
29
|
+
|
|
30
|
+
[tool.hatch.version]
|
|
31
|
+
path = "src/alku/__init__.py"
|
|
32
|
+
|
|
33
|
+
[tool.hatch.build.targets.sdist]
|
|
34
|
+
only-include = ["src/alku", "CHANGELOG.md"]
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.targets.wheel]
|
|
37
|
+
packages = ["src/alku"]
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = ["pytest>=8.4,<10", "pytest-cov>=6,<8", "ruff", "ty"]
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
addopts = ["--cov=alku", "--cov-report=term-missing", "--cov-fail-under=100"]
|
|
44
|
+
testpaths = ["tests"]
|
|
45
|
+
|
|
46
|
+
[tool.coverage.run]
|
|
47
|
+
source = ["alku"]
|
|
48
|
+
|
|
49
|
+
[tool.coverage.report]
|
|
50
|
+
fail_under = 100
|
|
51
|
+
show_missing = true
|
|
52
|
+
skip_covered = true
|
|
53
|
+
|
|
54
|
+
[tool.uv]
|
|
55
|
+
exclude-newer = "1 week"
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"""Managed project-scoped agent profiles for supported model providers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import stat
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from .providers import profile_for
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AgentConfigSyncError(ValueError):
|
|
13
|
+
"""A managed agent profile cannot be synchronized safely."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class AgentSpec:
|
|
18
|
+
name: str
|
|
19
|
+
description: str
|
|
20
|
+
model: str
|
|
21
|
+
effort: str
|
|
22
|
+
instructions: str
|
|
23
|
+
read_only: bool = False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class AgentConfigSyncReport:
|
|
28
|
+
state: str
|
|
29
|
+
copied: tuple[str, ...] = ()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _agent_specs(provider: str) -> tuple[AgentSpec, ...]:
|
|
33
|
+
"""Build native agent specs from the packaged model matrix."""
|
|
34
|
+
profile = profile_for(provider)
|
|
35
|
+
if provider == "openai":
|
|
36
|
+
workflow_instructions = (
|
|
37
|
+
"Read and follow .agents/skills/alku-workflow/SKILL.md. Own one bounded "
|
|
38
|
+
"feature, retain architecture and documentation context, and return a "
|
|
39
|
+
"verified result to the parent agent."
|
|
40
|
+
)
|
|
41
|
+
research_instructions = (
|
|
42
|
+
"Read and follow .agents/skills/alku-workflow/references/research.md. "
|
|
43
|
+
"Stay within the delegated question and return concise, cited evidence."
|
|
44
|
+
)
|
|
45
|
+
review_instructions = (
|
|
46
|
+
"Read and follow .agents/skills/alku-workflow/references/review.md. "
|
|
47
|
+
"Review the post-generation artifact independently and lead with concrete "
|
|
48
|
+
"findings."
|
|
49
|
+
)
|
|
50
|
+
develop_instructions = (
|
|
51
|
+
"Read and follow .agents/skills/alku-workflow/references/develop.md. "
|
|
52
|
+
"Implement only the assigned files and acceptance criteria, preserve "
|
|
53
|
+
"unrelated changes, and report targeted verification."
|
|
54
|
+
)
|
|
55
|
+
else:
|
|
56
|
+
workflow_instructions = (
|
|
57
|
+
"Follow the preloaded alku-workflow skill. Own one bounded feature, retain "
|
|
58
|
+
"architecture and documentation context, and return a verified result to "
|
|
59
|
+
"the parent agent."
|
|
60
|
+
)
|
|
61
|
+
research_instructions = (
|
|
62
|
+
"Read .claude/skills/alku-workflow/references/research.md. Stay within the "
|
|
63
|
+
"delegated question and return concise, cited evidence."
|
|
64
|
+
)
|
|
65
|
+
review_instructions = (
|
|
66
|
+
"Read .claude/skills/alku-workflow/references/review.md. Review the "
|
|
67
|
+
"post-generation artifact independently and lead with concrete findings."
|
|
68
|
+
)
|
|
69
|
+
develop_instructions = (
|
|
70
|
+
"Read .claude/skills/alku-workflow/references/develop.md. Implement only "
|
|
71
|
+
"the assigned files and acceptance criteria, preserve unrelated changes, "
|
|
72
|
+
"and report targeted verification."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
specs = [
|
|
76
|
+
AgentSpec(
|
|
77
|
+
"alku-workflow",
|
|
78
|
+
"Own one bounded Alku feature from approved plan through verification.",
|
|
79
|
+
profile.model_for("workflow"),
|
|
80
|
+
profile.effort_for("workflow"),
|
|
81
|
+
workflow_instructions,
|
|
82
|
+
),
|
|
83
|
+
AgentSpec(
|
|
84
|
+
"alku-research",
|
|
85
|
+
"Investigate a bounded question and return evidence without editing code.",
|
|
86
|
+
profile.model_for("research"),
|
|
87
|
+
profile.effort_for("research"),
|
|
88
|
+
research_instructions,
|
|
89
|
+
True,
|
|
90
|
+
),
|
|
91
|
+
AgentSpec(
|
|
92
|
+
"alku-review",
|
|
93
|
+
"Independently review a completed Alku change for material risks.",
|
|
94
|
+
profile.model_for("review"),
|
|
95
|
+
profile.effort_for("review"),
|
|
96
|
+
review_instructions,
|
|
97
|
+
True,
|
|
98
|
+
),
|
|
99
|
+
]
|
|
100
|
+
specs.extend(
|
|
101
|
+
AgentSpec(
|
|
102
|
+
f"alku-develop-{effort}",
|
|
103
|
+
f"Implement bounded Alku work at {effort} reasoning effort.",
|
|
104
|
+
profile.model_for(f"develop-{effort}"),
|
|
105
|
+
profile.effort_for(f"develop-{effort}"),
|
|
106
|
+
develop_instructions,
|
|
107
|
+
)
|
|
108
|
+
for effort in ("low", "medium", "high")
|
|
109
|
+
)
|
|
110
|
+
return tuple(specs)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
OPENAI_AGENT_SPECS = _agent_specs("openai")
|
|
114
|
+
ANTHROPIC_AGENT_SPECS = _agent_specs("anthropic")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _kind(path: Path) -> str:
|
|
118
|
+
try:
|
|
119
|
+
mode = path.lstat().st_mode
|
|
120
|
+
except FileNotFoundError:
|
|
121
|
+
return "missing"
|
|
122
|
+
except OSError:
|
|
123
|
+
return "other"
|
|
124
|
+
if stat.S_ISLNK(mode):
|
|
125
|
+
return "symlink"
|
|
126
|
+
if stat.S_ISREG(mode):
|
|
127
|
+
return "file"
|
|
128
|
+
if stat.S_ISDIR(mode):
|
|
129
|
+
return "directory"
|
|
130
|
+
return "other"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _safe_parent(root: Path, path: Path) -> bool:
|
|
134
|
+
try:
|
|
135
|
+
relative = path.parent.relative_to(root)
|
|
136
|
+
except ValueError:
|
|
137
|
+
return False
|
|
138
|
+
current = root
|
|
139
|
+
for part in relative.parts:
|
|
140
|
+
current /= part
|
|
141
|
+
if _kind(current) in {"symlink", "file", "other"}:
|
|
142
|
+
return False
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _toml(spec: AgentSpec) -> str:
|
|
147
|
+
sandbox = 'sandbox_mode = "read-only"\n' if spec.read_only else ""
|
|
148
|
+
return (
|
|
149
|
+
"# alku:managed-agent\n"
|
|
150
|
+
f'name = "{spec.name}"\n'
|
|
151
|
+
f'description = "{spec.description}"\n'
|
|
152
|
+
f'model = "{spec.model}"\n'
|
|
153
|
+
f'model_reasoning_effort = "{spec.effort}"\n'
|
|
154
|
+
f"{sandbox}"
|
|
155
|
+
'developer_instructions = """\n'
|
|
156
|
+
f"{spec.instructions}\n"
|
|
157
|
+
'"""\n'
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _markdown(spec: AgentSpec) -> str:
|
|
162
|
+
permission = "\npermissionMode: plan" if spec.read_only else ""
|
|
163
|
+
skills = "\nskills:\n - alku-workflow" if spec.name == "alku-workflow" else ""
|
|
164
|
+
return (
|
|
165
|
+
"---\n"
|
|
166
|
+
f"name: {spec.name}\n"
|
|
167
|
+
f"description: {spec.description}\n"
|
|
168
|
+
f"model: {spec.model}\n"
|
|
169
|
+
f"effort: {spec.effort}"
|
|
170
|
+
f"{permission}{skills}\n"
|
|
171
|
+
"---\n"
|
|
172
|
+
"<!-- alku:managed-agent -->\n\n"
|
|
173
|
+
f"{spec.instructions}\n"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def agent_config_outputs(root: Path) -> tuple[tuple[Path, str], ...]:
|
|
178
|
+
"""Return every provider-native project agent profile."""
|
|
179
|
+
outputs = [
|
|
180
|
+
(root / ".codex" / "agents" / f"{spec.name}.toml", _toml(spec))
|
|
181
|
+
for spec in OPENAI_AGENT_SPECS
|
|
182
|
+
]
|
|
183
|
+
outputs.extend(
|
|
184
|
+
(root / ".claude" / "agents" / f"{spec.name}.md", _markdown(spec))
|
|
185
|
+
for spec in ANTHROPIC_AGENT_SPECS
|
|
186
|
+
)
|
|
187
|
+
return tuple(outputs)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _managed(path: Path, content: str) -> bool:
|
|
191
|
+
if path.suffix == ".toml":
|
|
192
|
+
return content.startswith("# alku:managed-agent\n")
|
|
193
|
+
if not content.startswith("---\n"):
|
|
194
|
+
return False
|
|
195
|
+
end = content.find("\n---\n", 4)
|
|
196
|
+
return end >= 0 and content[end + 5 :].startswith("<!-- alku:managed-agent -->\n")
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def sync_agent_configs_report(
|
|
200
|
+
root: Path, *, dry_run: bool = False
|
|
201
|
+
) -> AgentConfigSyncReport:
|
|
202
|
+
"""Synchronize both provider profile sets with whole-file ownership checks."""
|
|
203
|
+
planned: list[tuple[Path, str, str | None]] = []
|
|
204
|
+
for path, content in agent_config_outputs(root):
|
|
205
|
+
if not _safe_parent(root, path):
|
|
206
|
+
raise AgentConfigSyncError(f"unsafe managed agent parent: {path.parent}")
|
|
207
|
+
kind = _kind(path)
|
|
208
|
+
if kind == "missing":
|
|
209
|
+
planned.append((path, content, None))
|
|
210
|
+
continue
|
|
211
|
+
if kind != "file":
|
|
212
|
+
raise AgentConfigSyncError(f"unsafe managed agent collision: {path}")
|
|
213
|
+
try:
|
|
214
|
+
actual = path.read_text(encoding="utf-8")
|
|
215
|
+
except (OSError, UnicodeDecodeError) as error:
|
|
216
|
+
raise AgentConfigSyncError(
|
|
217
|
+
f"cannot read managed agent {path}: {error}"
|
|
218
|
+
) from error
|
|
219
|
+
if actual == content:
|
|
220
|
+
continue
|
|
221
|
+
if not _managed(path, actual):
|
|
222
|
+
raise AgentConfigSyncError(
|
|
223
|
+
f"fail-closed agent collision: unmarked/user-owned file: {path}"
|
|
224
|
+
)
|
|
225
|
+
planned.append((path, content, actual))
|
|
226
|
+
if not dry_run:
|
|
227
|
+
for path, content, original in planned:
|
|
228
|
+
if not _safe_parent(root, path):
|
|
229
|
+
raise AgentConfigSyncError(
|
|
230
|
+
f"concurrent managed agent parent update: {path.parent}"
|
|
231
|
+
)
|
|
232
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
233
|
+
if original is None:
|
|
234
|
+
if _kind(path) != "missing":
|
|
235
|
+
raise AgentConfigSyncError(
|
|
236
|
+
f"concurrent managed agent update: {path}"
|
|
237
|
+
)
|
|
238
|
+
with path.open("x", encoding="utf-8", newline="") as stream:
|
|
239
|
+
stream.write(content)
|
|
240
|
+
continue
|
|
241
|
+
if _kind(path) != "file":
|
|
242
|
+
raise AgentConfigSyncError(f"concurrent managed agent update: {path}")
|
|
243
|
+
try:
|
|
244
|
+
current = path.read_text(encoding="utf-8")
|
|
245
|
+
except (OSError, UnicodeDecodeError) as error:
|
|
246
|
+
raise AgentConfigSyncError(
|
|
247
|
+
f"cannot recheck managed agent {path}: {error}"
|
|
248
|
+
) from error
|
|
249
|
+
if current != original or not _managed(path, current):
|
|
250
|
+
raise AgentConfigSyncError(f"concurrent managed agent update: {path}")
|
|
251
|
+
temporary = path.with_name(path.name + ".alku-new")
|
|
252
|
+
if _kind(temporary) == "missing":
|
|
253
|
+
with temporary.open("x", encoding="utf-8", newline="") as stream:
|
|
254
|
+
stream.write(content)
|
|
255
|
+
elif (
|
|
256
|
+
_kind(temporary) != "file"
|
|
257
|
+
or temporary.read_text(encoding="utf-8") != content
|
|
258
|
+
):
|
|
259
|
+
raise AgentConfigSyncError(
|
|
260
|
+
f"unsafe managed agent staging collision: {temporary}"
|
|
261
|
+
)
|
|
262
|
+
if _kind(path) != "file" or path.read_text(encoding="utf-8") != original:
|
|
263
|
+
temporary.unlink(missing_ok=True)
|
|
264
|
+
raise AgentConfigSyncError(f"concurrent managed agent update: {path}")
|
|
265
|
+
temporary.replace(path)
|
|
266
|
+
return AgentConfigSyncReport(
|
|
267
|
+
"copied" if planned else "unchanged",
|
|
268
|
+
tuple(str(path.relative_to(root)) for path, _, _ in planned),
|
|
269
|
+
)
|