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.
Files changed (66) hide show
  1. alku-0.5.0/.gitignore +11 -0
  2. alku-0.5.0/CHANGELOG.md +102 -0
  3. alku-0.5.0/LICENSE +21 -0
  4. alku-0.5.0/PKG-INFO +92 -0
  5. alku-0.5.0/README.md +69 -0
  6. alku-0.5.0/pyproject.toml +55 -0
  7. alku-0.5.0/src/alku/__init__.py +3 -0
  8. alku-0.5.0/src/alku/__main__.py +4 -0
  9. alku-0.5.0/src/alku/agent_configs.py +269 -0
  10. alku-0.5.0/src/alku/claude.py +144 -0
  11. alku-0.5.0/src/alku/cli.py +35 -0
  12. alku-0.5.0/src/alku/codebase_memory.py +51 -0
  13. alku-0.5.0/src/alku/commands/__init__.py +1 -0
  14. alku-0.5.0/src/alku/commands/common.py +144 -0
  15. alku-0.5.0/src/alku/commands/docs.py +244 -0
  16. alku-0.5.0/src/alku/commands/init.py +67 -0
  17. alku-0.5.0/src/alku/commands/self.py +45 -0
  18. alku-0.5.0/src/alku/file_transactions.py +484 -0
  19. alku-0.5.0/src/alku/gate.py +230 -0
  20. alku-0.5.0/src/alku/generation/__init__.py +5 -0
  21. alku-0.5.0/src/alku/generation/diagnostics.py +252 -0
  22. alku-0.5.0/src/alku/generation/orchestration.py +396 -0
  23. alku-0.5.0/src/alku/generation/rendering.py +241 -0
  24. alku-0.5.0/src/alku/generation/transactions.py +588 -0
  25. alku-0.5.0/src/alku/markdown.py +208 -0
  26. alku-0.5.0/src/alku/migrations/__init__.py +26 -0
  27. alku-0.5.0/src/alku/migrations/base.py +58 -0
  28. alku-0.5.0/src/alku/migrations/registry.py +7 -0
  29. alku-0.5.0/src/alku/migrations/runner.py +103 -0
  30. alku-0.5.0/src/alku/model_config.py +212 -0
  31. alku-0.5.0/src/alku/models.py +125 -0
  32. alku-0.5.0/src/alku/models.yml +28 -0
  33. alku-0.5.0/src/alku/paths.py +102 -0
  34. alku-0.5.0/src/alku/pipeline/__init__.py +25 -0
  35. alku-0.5.0/src/alku/pipeline/base.py +97 -0
  36. alku-0.5.0/src/alku/pipeline/context.py +162 -0
  37. alku-0.5.0/src/alku/pipeline/runner.py +136 -0
  38. alku-0.5.0/src/alku/pipeline/steps.py +396 -0
  39. alku-0.5.0/src/alku/proposal/__init__.py +1 -0
  40. alku-0.5.0/src/alku/proposal/identifiers.py +96 -0
  41. alku-0.5.0/src/alku/proposal/planning.py +78 -0
  42. alku-0.5.0/src/alku/proposal/rewriting.py +414 -0
  43. alku-0.5.0/src/alku/proposal/transaction.py +231 -0
  44. alku-0.5.0/src/alku/proposal/types.py +65 -0
  45. alku-0.5.0/src/alku/providers.py +147 -0
  46. alku-0.5.0/src/alku/queries.py +257 -0
  47. alku-0.5.0/src/alku/repository.py +345 -0
  48. alku-0.5.0/src/alku/skill_catalog.py +154 -0
  49. alku-0.5.0/src/alku/skill_planning.py +203 -0
  50. alku-0.5.0/src/alku/skill_sync.py +134 -0
  51. alku-0.5.0/src/alku/skill_types.py +37 -0
  52. alku-0.5.0/src/alku/skill_writes.py +262 -0
  53. alku-0.5.0/src/alku/skills/alku-architecture/SKILL.md +70 -0
  54. alku-0.5.0/src/alku/skills/alku-auto/SKILL.md +58 -0
  55. alku-0.5.0/src/alku/skills/alku-brainstorm/SKILL.md +24 -0
  56. alku-0.5.0/src/alku/skills/alku-docs/SKILL.md +20 -0
  57. alku-0.5.0/src/alku/skills/alku-workflow/SKILL.md +57 -0
  58. alku-0.5.0/src/alku/skills/alku-workflow/references/develop.md +15 -0
  59. alku-0.5.0/src/alku/skills/alku-workflow/references/research.md +15 -0
  60. alku-0.5.0/src/alku/skills/alku-workflow/references/review.md +20 -0
  61. alku-0.5.0/src/alku/structure.py +150 -0
  62. alku-0.5.0/src/alku/templates/README.md +76 -0
  63. alku-0.5.0/src/alku/templates/gate.md +8 -0
  64. alku-0.5.0/src/alku/tooling.py +425 -0
  65. alku-0.5.0/src/alku/updates.py +244 -0
  66. alku-0.5.0/src/alku/validation.py +559 -0
alku-0.5.0/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ .venv/
2
+ .idea/
3
+ dist/
4
+ __pycache__/
5
+ *.py[cod]
6
+ .coverage
7
+ .coverage.*
8
+ htmlcov/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .pypirc
@@ -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,3 @@
1
+ """Alku repository bootstrapper and documentation maintainer."""
2
+
3
+ __version__ = "0.5.0"
@@ -0,0 +1,4 @@
1
+ from .cli import cli
2
+
3
+ if __name__ == "__main__":
4
+ cli()
@@ -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
+ )