shikiru-cli 0.2.0__py3-none-any.whl
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.
- shikiru/__init__.py +1 -0
- shikiru/__main__.py +8 -0
- shikiru/agents/__init__.py +3 -0
- shikiru/agents/contracts.py +590 -0
- shikiru/agents/registry.py +87 -0
- shikiru/agents/sdk/__init__.py +17 -0
- shikiru/agents/sdk/reset_time.py +137 -0
- shikiru/agents/sdk/turn.py +335 -0
- shikiru/agents/settings.py +151 -0
- shikiru/agents/testing/__init__.py +26 -0
- shikiru/agents/testing/contract_suite.py +653 -0
- shikiru/agents/testing/resume.py +15 -0
- shikiru/bootstrap.py +1277 -0
- shikiru/cli/__init__.py +1 -0
- shikiru/cli/app.py +770 -0
- shikiru/cli/context.py +63 -0
- shikiru/cli/render.py +1475 -0
- shikiru/cli/report.py +156 -0
- shikiru/cli/session_prompt.py +63 -0
- shikiru/cli/view.py +131 -0
- shikiru/clock.py +41 -0
- shikiru/config/__init__.py +8 -0
- shikiru/config/defaults.toml +131 -0
- shikiru/config/loader.py +178 -0
- shikiru/config/settings.py +392 -0
- shikiru/dashboard/__init__.py +3 -0
- shikiru/dashboard/actions_list.py +799 -0
- shikiru/dashboard/activity.py +150 -0
- shikiru/dashboard/app.py +1765 -0
- shikiru/dashboard/blocker_evidence.py +122 -0
- shikiru/dashboard/blocker_rows.py +84 -0
- shikiru/dashboard/briefing_view.py +871 -0
- shikiru/dashboard/contract.py +1086 -0
- shikiru/dashboard/covered.py +32 -0
- shikiru/dashboard/detail.py +1209 -0
- shikiru/dashboard/detail_fold.py +244 -0
- shikiru/dashboard/detail_hint.py +68 -0
- shikiru/dashboard/details_list_view.py +150 -0
- shikiru/dashboard/diagnostics.py +80 -0
- shikiru/dashboard/diagnostics_sections.py +186 -0
- shikiru/dashboard/evidence_table.py +123 -0
- shikiru/dashboard/extra_details.py +214 -0
- shikiru/dashboard/fault_guidance.py +216 -0
- shikiru/dashboard/fold_bodies.py +276 -0
- shikiru/dashboard/help_keys.py +166 -0
- shikiru/dashboard/help_screen.py +204 -0
- shikiru/dashboard/interaction.py +17 -0
- shikiru/dashboard/ladder_editor.py +544 -0
- shikiru/dashboard/live_settings.py +62 -0
- shikiru/dashboard/motion.py +368 -0
- shikiru/dashboard/orchestration_fields.py +201 -0
- shikiru/dashboard/palette.py +1606 -0
- shikiru/dashboard/palette_app.py +89 -0
- shikiru/dashboard/palette_drafts.py +33 -0
- shikiru/dashboard/resolution.py +573 -0
- shikiru/dashboard/resolution_view.py +302 -0
- shikiru/dashboard/row_list.py +256 -0
- shikiru/dashboard/section_rows.py +173 -0
- shikiru/dashboard/services.py +665 -0
- shikiru/dashboard/session_history.py +402 -0
- shikiru/dashboard/shikiru_init_ascii_art.txt +40 -0
- shikiru/dashboard/task_picker.py +358 -0
- shikiru/dashboard/tick.py +59 -0
- shikiru/dashboard/timeline_filter.py +129 -0
- shikiru/dashboard/timeline_section.py +385 -0
- shikiru/dashboard/usage_body.py +269 -0
- shikiru/dashboard/views.py +2314 -0
- shikiru/dashboard/wizard.py +1389 -0
- shikiru/domain/__init__.py +1 -0
- shikiru/domain/acceptance.py +130 -0
- shikiru/domain/activity.py +28 -0
- shikiru/domain/attempts.py +333 -0
- shikiru/domain/briefing.py +257 -0
- shikiru/domain/commands.py +219 -0
- shikiru/domain/decisions.py +231 -0
- shikiru/domain/docker.py +262 -0
- shikiru/domain/engine_uptime.py +87 -0
- shikiru/domain/escalation.py +194 -0
- shikiru/domain/escalation_evidence.py +186 -0
- shikiru/domain/escalation_panel.py +144 -0
- shikiru/domain/events.py +71 -0
- shikiru/domain/evidence_export.py +89 -0
- shikiru/domain/faults.py +86 -0
- shikiru/domain/feedback.py +180 -0
- shikiru/domain/feedback_rounds.py +172 -0
- shikiru/domain/fencing.py +31 -0
- shikiru/domain/ladder.py +102 -0
- shikiru/domain/ladder_text.py +44 -0
- shikiru/domain/levels.py +44 -0
- shikiru/domain/motion.py +199 -0
- shikiru/domain/packet_evidence.py +377 -0
- shikiru/domain/pushes.py +47 -0
- shikiru/domain/quota_blocks.py +44 -0
- shikiru/domain/quota_waits.py +219 -0
- shikiru/domain/recovery.py +194 -0
- shikiru/domain/reports.py +498 -0
- shikiru/domain/results.py +217 -0
- shikiru/domain/runs.py +85 -0
- shikiru/domain/scheduling.py +231 -0
- shikiru/domain/status_tasks.py +40 -0
- shikiru/domain/steps.py +325 -0
- shikiru/domain/tasks.py +174 -0
- shikiru/domain/timeline.py +208 -0
- shikiru/domain/timeline_phrases.py +271 -0
- shikiru/domain/versions.py +18 -0
- shikiru/domain/workflow.py +372 -0
- shikiru/engine/__init__.py +0 -0
- shikiru/engine/acceptance.py +90 -0
- shikiru/engine/agents.py +61 -0
- shikiru/engine/cosmetic_decorator.py +99 -0
- shikiru/engine/daemon.py +409 -0
- shikiru/engine/dependency.py +37 -0
- shikiru/engine/escalation_filing.py +110 -0
- shikiru/engine/escalations.py +401 -0
- shikiru/engine/events.py +285 -0
- shikiru/engine/evidence.py +81 -0
- shikiru/engine/fault_reporter.py +144 -0
- shikiru/engine/feedback.py +577 -0
- shikiru/engine/feedback_audit.py +80 -0
- shikiru/engine/feedback_intake.py +95 -0
- shikiru/engine/feedback_snapshot.py +148 -0
- shikiru/engine/flow.py +87 -0
- shikiru/engine/handlers/__init__.py +33 -0
- shikiru/engine/handlers/base.py +148 -0
- shikiru/engine/handlers/engine.py +24 -0
- shikiru/engine/handlers/escalations.py +174 -0
- shikiru/engine/handlers/operator.py +81 -0
- shikiru/engine/handlers/tasks.py +237 -0
- shikiru/engine/history.py +128 -0
- shikiru/engine/integration.py +1234 -0
- shikiru/engine/integration_turns.py +337 -0
- shikiru/engine/layout_guard.py +362 -0
- shikiru/engine/lifecycle.py +60 -0
- shikiru/engine/monitor.py +317 -0
- shikiru/engine/notification_delivery.py +124 -0
- shikiru/engine/notification_feed.py +57 -0
- shikiru/engine/notifier.py +91 -0
- shikiru/engine/pull_request_closing.py +117 -0
- shikiru/engine/push_service.py +177 -0
- shikiru/engine/quota_probe_threads.py +79 -0
- shikiru/engine/quota_reconciler.py +210 -0
- shikiru/engine/quota_schedule.py +24 -0
- shikiru/engine/quota_waits.py +102 -0
- shikiru/engine/reconcile.py +91 -0
- shikiru/engine/recovery.py +284 -0
- shikiru/engine/retention.py +102 -0
- shikiru/engine/root_preparation.py +86 -0
- shikiru/engine/scheduler.py +320 -0
- shikiru/engine/session_guard.py +46 -0
- shikiru/engine/sessions.py +95 -0
- shikiru/engine/signalled.py +75 -0
- shikiru/engine/step_runner.py +140 -0
- shikiru/engine/supervisor.py +101 -0
- shikiru/engine/takeover.py +223 -0
- shikiru/engine/task_facts.py +46 -0
- shikiru/engine/tick_guard.py +95 -0
- shikiru/engine/titles.py +297 -0
- shikiru/engine/transitions.py +79 -0
- shikiru/engine/turn_context.py +206 -0
- shikiru/engine/turn_execution.py +30 -0
- shikiru/engine/turn_loop.py +230 -0
- shikiru/engine/turn_processes.py +79 -0
- shikiru/engine/turns.py +457 -0
- shikiru/errors.py +144 -0
- shikiru/helper/__init__.py +3 -0
- shikiru/helper/helper.py +182 -0
- shikiru/helper/models.py +11 -0
- shikiru/mcp/__init__.py +1 -0
- shikiru/mcp/server.py +196 -0
- shikiru/operator/__init__.py +33 -0
- shikiru/operator/attachment.py +24 -0
- shikiru/operator/commands.py +319 -0
- shikiru/operator/detectors.py +374 -0
- shikiru/operator/doctor.py +351 -0
- shikiru/operator/events.py +57 -0
- shikiru/operator/evidence_export.py +498 -0
- shikiru/operator/evidence_writer.py +78 -0
- shikiru/operator/lifecycle.py +294 -0
- shikiru/operator/plugin_checks.py +194 -0
- shikiru/operator/probe.py +287 -0
- shikiru/operator/queries.py +1601 -0
- shikiru/operator/services.py +36 -0
- shikiru/operator/sessions.py +378 -0
- shikiru/operator/strip_views.py +162 -0
- shikiru/operator/timeline.py +416 -0
- shikiru/operator/toml_text.py +62 -0
- shikiru/palette.py +18 -0
- shikiru/paths.py +91 -0
- shikiru/plugins/__init__.py +0 -0
- shikiru/plugins/agents/__init__.py +1 -0
- shikiru/plugins/agents/claude/__init__.py +5 -0
- shikiru/plugins/agents/claude/agent.py +251 -0
- shikiru/plugins/agents/claude/context.py +116 -0
- shikiru/plugins/agents/claude/headless.py +92 -0
- shikiru/plugins/agents/claude/hook.py +57 -0
- shikiru/plugins/agents/claude/model_catalog.py +105 -0
- shikiru/plugins/agents/claude/options.py +62 -0
- shikiru/plugins/agents/claude/turn.py +1018 -0
- shikiru/plugins/agents/codex/__init__.py +5 -0
- shikiru/plugins/agents/codex/agent.py +486 -0
- shikiru/plugins/agents/codex/headless.py +166 -0
- shikiru/plugins/agents/codex/home.py +14 -0
- shikiru/plugins/agents/codex/model_catalog.py +128 -0
- shikiru/plugins/agents/codex/options.py +94 -0
- shikiru/plugins/agents/codex/quota.py +195 -0
- shikiru/plugins/agents/codex/sandbox.py +31 -0
- shikiru/plugins/agents/codex/sandbox_probe.py +97 -0
- shikiru/plugins/agents/codex/turn.py +767 -0
- shikiru/plugins/agents/codex/usage.py +106 -0
- shikiru/plugins/agents/shared_settings.py +62 -0
- shikiru/plugins/widgets/__init__.py +3 -0
- shikiru/plugins/widgets/dialogue.py +144 -0
- shikiru/plugins/widgets/disk.py +123 -0
- shikiru/plugins/widgets/docker.py +225 -0
- shikiru/plugins/widgets/escalations.py +402 -0
- shikiru/plugins/widgets/faults.py +204 -0
- shikiru/plugins/widgets/header.py +321 -0
- shikiru/plugins/widgets/queue.py +1140 -0
- shikiru/plugins/widgets/queue_hint.py +93 -0
- shikiru/plugins/widgets/resources.py +105 -0
- shikiru/plugins/widgets/strips.py +82 -0
- shikiru/plugins/widgets/usage.py +176 -0
- shikiru/private.py +94 -0
- shikiru/process.py +535 -0
- shikiru/prompts/__init__.py +3 -0
- shikiru/prompts/renderer.py +85 -0
- shikiru/prompts/templates/_base.md.j2 +17 -0
- shikiru/prompts/templates/_macros.md.j2 +61 -0
- shikiru/prompts/templates/conflicts.md.j2 +22 -0
- shikiru/prompts/templates/consult.md.j2 +5 -0
- shikiru/prompts/templates/continue.md.j2 +4 -0
- shikiru/prompts/templates/feedback_analysis.md.j2 +29 -0
- shikiru/prompts/templates/feedback_rework.md.j2 +33 -0
- shikiru/prompts/templates/feedback_verify.md.j2 +33 -0
- shikiru/prompts/templates/handoff.md.j2 +23 -0
- shikiru/prompts/templates/implement.md.j2 +13 -0
- shikiru/prompts/templates/plan.md.j2 +30 -0
- shikiru/prompts/templates/rework.md.j2 +13 -0
- shikiru/prompts/templates/triage.md.j2 +20 -0
- shikiru/prompts/templates/verify.md.j2 +29 -0
- shikiru/store/__init__.py +1 -0
- shikiru/store/commands.py +242 -0
- shikiru/store/database.py +297 -0
- shikiru/store/escalations.py +240 -0
- shikiru/store/events.py +213 -0
- shikiru/store/feedback.py +266 -0
- shikiru/store/migrations/001_initial.sql +63 -0
- shikiru/store/migrations/002_headless_turns.sql +31 -0
- shikiru/store/migrations/003_drop_command_handled_at.sql +1 -0
- shikiru/store/migrations/004_repo_base_ref.sql +3 -0
- shikiru/store/migrations/005_complexity_explicit.sql +3 -0
- shikiru/store/migrations/006_events_kind_index.sql +1 -0
- shikiru/store/migrations/007_notification_cursor.sql +1 -0
- shikiru/store/migrations/008_reported_usage.sql +1 -0
- shikiru/store/migrations/009_billing_declarations.sql +2 -0
- shikiru/store/migrations/010_canonical_quota_windows.sql +3 -0
- shikiru/store/migrations/012_drop_task_quota_evidence.sql +2 -0
- shikiru/store/migrations/013_push_targets.sql +7 -0
- shikiru/store/migrations/014_agent_block_time.sql +6 -0
- shikiru/store/migrations/015_repair_orphan_agent_blocks.sql +3 -0
- shikiru/store/migrations/016_feedback_rounds.sql +29 -0
- shikiru/store/migrations/017_feedback_round_comments.sql +5 -0
- shikiru/store/migrations/__init__.py +1 -0
- shikiru/store/notification_cursor.py +29 -0
- shikiru/store/pushes.py +117 -0
- shikiru/store/quotas.py +153 -0
- shikiru/store/reader.py +310 -0
- shikiru/store/retention.py +92 -0
- shikiru/store/runs.py +227 -0
- shikiru/store/tasks.py +949 -0
- shikiru/telemetry/__init__.py +1 -0
- shikiru/telemetry/disk.py +167 -0
- shikiru/telemetry/docker.py +164 -0
- shikiru/telemetry/resources.py +296 -0
- shikiru/tmux/__init__.py +3 -0
- shikiru/tmux/client.py +637 -0
- shikiru/tmux/decorator.py +225 -0
- shikiru/tmux/layout.py +200 -0
- shikiru/tmux/session.py +381 -0
- shikiru/ui/__init__.py +9 -0
- shikiru/ui/age.py +45 -0
- shikiru/ui/bubbles.py +41 -0
- shikiru/ui/compact.py +1209 -0
- shikiru/ui/console.py +49 -0
- shikiru/ui/fold.py +50 -0
- shikiru/ui/gated_static.py +117 -0
- shikiru/ui/gauge.py +40 -0
- shikiru/ui/glyphs.py +109 -0
- shikiru/ui/keys.py +57 -0
- shikiru/ui/live_interval.py +42 -0
- shikiru/ui/popup.py +311 -0
- shikiru/ui/quota_status.py +26 -0
- shikiru/ui/recent_window.py +78 -0
- shikiru/ui/screen.py +57 -0
- shikiru/ui/section.py +193 -0
- shikiru/ui/session.py +65 -0
- shikiru/ui/size.py +17 -0
- shikiru/ui/splash.py +42 -0
- shikiru/ui/status.py +47 -0
- shikiru/ui/status_strip.py +149 -0
- shikiru/ui/stream.py +152 -0
- shikiru/ui/strip.py +332 -0
- shikiru/ui/theme.py +83 -0
- shikiru/ui/tokens.py +51 -0
- shikiru/view_log.py +126 -0
- shikiru/workspace/__init__.py +3 -0
- shikiru/workspace/base_refs.py +160 -0
- shikiru/workspace/caches.py +136 -0
- shikiru/workspace/facts.py +118 -0
- shikiru/workspace/git.py +601 -0
- shikiru/workspace/github.py +744 -0
- shikiru/workspace/messages.py +107 -0
- shikiru/workspace/reports.py +145 -0
- shikiru/workspace/roots.py +180 -0
- shikiru_cli-0.2.0.dist-info/METADATA +428 -0
- shikiru_cli-0.2.0.dist-info/RECORD +319 -0
- shikiru_cli-0.2.0.dist-info/WHEEL +4 -0
- shikiru_cli-0.2.0.dist-info/entry_points.txt +18 -0
- shikiru_cli-0.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{% extends "_base.md.j2" %}
|
|
2
|
+
{% import "_macros.md.j2" as macros %}
|
|
3
|
+
{% block body %}
|
|
4
|
+
You are the Implementer resolving a rebase conflict during integration of this task.
|
|
5
|
+
|
|
6
|
+
{% if conflict %}
|
|
7
|
+
The latest conflict or verification guidance is:
|
|
8
|
+
|
|
9
|
+
{{ conflict | fenced("text") }}
|
|
10
|
+
{% endif %}
|
|
11
|
+
|
|
12
|
+
{{ macros.facts_section(facts) }}
|
|
13
|
+
|
|
14
|
+
Resolve the conflict, keeping the intent of both sides, then commit. Report `completed` only
|
|
15
|
+
when the rebase can proceed cleanly; report `needs_decision` or `blocked` with a concrete
|
|
16
|
+
`question` otherwise.
|
|
17
|
+
|
|
18
|
+
Before committing, check the target project's instructions and documentation for its commit-message
|
|
19
|
+
convention. Follow that convention when one is documented; use Conventional Commits if none is
|
|
20
|
+
documented. Keep the subject concise and summarizing the conflict resolution, and end the commit
|
|
21
|
+
message with the trailer `Task: {{ task_id }}` on its own line.
|
|
22
|
+
{% endblock %}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{% extends "_base.md.j2" %}
|
|
2
|
+
{% import "_macros.md.j2" as macros %}
|
|
3
|
+
{% block body %}
|
|
4
|
+
You are the Leader analysing the pull request review feedback below for this task. Task mode: `{{ mode }}`.
|
|
5
|
+
|
|
6
|
+
{{ macros.facts_section(facts) }}
|
|
7
|
+
|
|
8
|
+
## Feedback
|
|
9
|
+
|
|
10
|
+
{{ feedback_snapshot | fenced("json") }}
|
|
11
|
+
|
|
12
|
+
Decide every source item exactly once, by its `id`. A discussion is given whole, with the earlier
|
|
13
|
+
answers that were already posted in it. Its messages marked `"new": true` are the ones to answer;
|
|
14
|
+
the unmarked ones are context, and `reply` answers only the marked ones. Each item may raise several points: list all of
|
|
15
|
+
them in `points` and cover each in `detail` and `reply`. Choose `address` for a request to accept,
|
|
16
|
+
`already_addressed` when the Git facts or files show it is done (a moved or outdated location is
|
|
17
|
+
not proof), `disagree` for a request to decline with a specific reason, and `needs_operator`
|
|
18
|
+
when evidence is missing or requirements conflict; never invent a fix for those.
|
|
19
|
+
|
|
20
|
+
Put each piece of implementation work in `work` with a concrete instruction, and point every
|
|
21
|
+
`address` decision at it by `work_id`. Duplicate requests share one `work` entry but keep their own
|
|
22
|
+
decision and `reply`. Do not change code or files. Replies are proposals; the engine publishes
|
|
23
|
+
them after verification.
|
|
24
|
+
|
|
25
|
+
{% if mode == "plan" %}
|
|
26
|
+
This is a planning task: accepted work stays within its planning and documentation scope. A request
|
|
27
|
+
to implement the planned product is a `needs_operator` decision about scope.
|
|
28
|
+
{% endif %}
|
|
29
|
+
{% endblock %}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{% extends "_base.md.j2" %}
|
|
2
|
+
{% import "_macros.md.j2" as macros %}
|
|
3
|
+
{% block body %}
|
|
4
|
+
You are the Implementer applying the accepted work from pull request review feedback. Task mode: `{{ mode }}`.
|
|
5
|
+
|
|
6
|
+
{{ macros.facts_section(facts) }}
|
|
7
|
+
|
|
8
|
+
## Work to do
|
|
9
|
+
{% for work in feedback_work %}
|
|
10
|
+
|
|
11
|
+
{{ work | fenced("json") }}
|
|
12
|
+
{% endfor %}
|
|
13
|
+
|
|
14
|
+
## Feedback it answers
|
|
15
|
+
|
|
16
|
+
{{ feedback_snapshot | fenced("json") }}
|
|
17
|
+
|
|
18
|
+
In a discussion, the messages marked `"new": true` are the ones the work answers; the unmarked ones
|
|
19
|
+
are context.
|
|
20
|
+
|
|
21
|
+
Do exactly this work, commit it, run the project's tests and keep the trackers current. Do not
|
|
22
|
+
reply to the reviewers or touch other feedback.
|
|
23
|
+
|
|
24
|
+
Before committing, check the target project's instructions and documentation for its commit-message
|
|
25
|
+
convention. Follow that convention when one is documented; use Conventional Commits if none is
|
|
26
|
+
documented. Keep each subject concise and summarizing its commit. End every commit message with the
|
|
27
|
+
trailer `Task: {{ task_id }}` on its own line.
|
|
28
|
+
{% if mode == "plan" %}
|
|
29
|
+
This is a planning task: stay within planning and documentation changes.
|
|
30
|
+
{% endif %}
|
|
31
|
+
Report `completed` only when nothing remains; report `needs_decision` or `blocked` with a concrete
|
|
32
|
+
`question` otherwise.
|
|
33
|
+
{% endblock %}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{% extends "_base.md.j2" %}
|
|
2
|
+
{% import "_macros.md.j2" as macros %}
|
|
3
|
+
{% block body %}
|
|
4
|
+
You are the Leader verifying the result of a pull request review feedback round. Task mode: `{{ mode }}`.
|
|
5
|
+
|
|
6
|
+
{{ macros.facts_section(facts) }}
|
|
7
|
+
|
|
8
|
+
## Feedback snapshot
|
|
9
|
+
|
|
10
|
+
{{ feedback_snapshot | fenced("json") }}
|
|
11
|
+
|
|
12
|
+
## Your analysis
|
|
13
|
+
|
|
14
|
+
{{ feedback_analysis | fenced("json") }}
|
|
15
|
+
|
|
16
|
+
Review the changes with `git diff {{ base }}...HEAD` in the workspace. The facts above outrank the
|
|
17
|
+
Implementer's claims. Check that each accepted request is really satisfied, that each
|
|
18
|
+
`already_addressed` claim is true, and that each reply is accurate against the snapshot and the
|
|
19
|
+
Git and check evidence. Do not change code or tests.
|
|
20
|
+
|
|
21
|
+
In a discussion, the messages marked `"new": true` are the ones each reply must answer; the
|
|
22
|
+
unmarked ones are context, including earlier answers.
|
|
23
|
+
|
|
24
|
+
Choose `accept` only when every change and reply is right; give the final reply for each source
|
|
25
|
+
item that has one in `replies`. Check the target project's instructions and documentation for its
|
|
26
|
+
commit-message convention. On `accept`, also give `commit_subject`: a concise subject for the whole
|
|
27
|
+
change following that convention, or Conventional Commits if none is documented (no task reference).
|
|
28
|
+
Choose `changes_requested` with `issues` for further rework, or `escalate` with options for the
|
|
29
|
+
Operator. `supersede` is not available.
|
|
30
|
+
{% if final_round %}
|
|
31
|
+
This is the final round: `changes_requested` is not available.
|
|
32
|
+
{% endif %}
|
|
33
|
+
{% endblock %}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{% extends "_base.md.j2" %}
|
|
2
|
+
{% import "_macros.md.j2" as macros %}
|
|
3
|
+
{% block body %}
|
|
4
|
+
You are picking up this task in a fresh session. Read the earlier reports and facts below to
|
|
5
|
+
catch up before doing anything else.
|
|
6
|
+
|
|
7
|
+
{{ macros.facts_section(facts) }}
|
|
8
|
+
|
|
9
|
+
{% if operator_decision is defined %}
|
|
10
|
+
{{ macros.consult_body(question, operator_decision) }}
|
|
11
|
+
{% else %}
|
|
12
|
+
Continue the task from here.
|
|
13
|
+
{% endif %}
|
|
14
|
+
|
|
15
|
+
For commits you make, check the target project's instructions and documentation for its
|
|
16
|
+
commit-message convention. Follow that convention when one is documented; use Conventional Commits
|
|
17
|
+
if none is documented. Keep each subject concise and summarizing its commit. End every commit message
|
|
18
|
+
with the trailer `Task: {{ task_id }}` on its own line.
|
|
19
|
+
|
|
20
|
+
For a plan or an accepted review, give `commit_subject` and `summary` for the whole planned or
|
|
21
|
+
accepted change. Keep the summary short. For an accepted review, draw on every implementation and
|
|
22
|
+
rework report above, not only the latest review.
|
|
23
|
+
{% endblock %}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{% extends "_base.md.j2" %}
|
|
2
|
+
{% block body %}
|
|
3
|
+
You are the Implementer, starting this task's first implementation turn.
|
|
4
|
+
|
|
5
|
+
Implement the request, commit your work, run the project's tests and keep the trackers
|
|
6
|
+
current. Report `completed` only when nothing remains; report `needs_decision` or `blocked`
|
|
7
|
+
with a concrete `question` otherwise.
|
|
8
|
+
|
|
9
|
+
Before committing, check the target project's instructions and documentation for its commit-message
|
|
10
|
+
convention. Follow that convention when one is documented; use Conventional Commits if none is
|
|
11
|
+
documented. Keep each subject concise and summarizing its commit. End every commit message with the
|
|
12
|
+
trailer `Task: {{ task_id }}` on its own line.
|
|
13
|
+
{% endblock %}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{% extends "_base.md.j2" %}
|
|
2
|
+
{% block body %}
|
|
3
|
+
You are the Leader planning a task.
|
|
4
|
+
|
|
5
|
+
Shape the plan in the trackers listed above and commit it. Fill `new_tasks` only when the
|
|
6
|
+
request itself asks you to execute the plan, not merely to produce one; otherwise leave it
|
|
7
|
+
empty. A plan turn implements nothing, so when the request asks for work to be done (for
|
|
8
|
+
example "implement ..."), `new_tasks` is the only way that work happens, even when the
|
|
9
|
+
trackers already describe it: leaving it empty ends this task with nothing implemented.
|
|
10
|
+
When you fill `new_tasks`, restructure the tracker entries so each new task has its own
|
|
11
|
+
entry and together they replace this task's entry; each new task ticks its own entry when done.
|
|
12
|
+
Record any tracker changes outside the plan itself in `scope_changes`.
|
|
13
|
+
|
|
14
|
+
Check the target project's instructions and documentation for its commit-message convention. Give
|
|
15
|
+
`commit_subject` a concise subject for the whole plan change using that convention, or Conventional
|
|
16
|
+
Commits if none is documented (no task reference). Keep `summary` short and describe the whole
|
|
17
|
+
committed plan change.
|
|
18
|
+
Use that subject for the plan commit, and end the commit message with the trailer
|
|
19
|
+
`Task: {{ task_id }}` on its own line.
|
|
20
|
+
|
|
21
|
+
Give each tracker entry for a task, and each `new_tasks` request, an acceptance-criteria section
|
|
22
|
+
in exactly this form: the heading verbatim, then one independently verifiable criterion per bullet.
|
|
23
|
+
|
|
24
|
+
## {{ acceptance_criteria_heading }}
|
|
25
|
+
- <criterion>
|
|
26
|
+
- <criterion>
|
|
27
|
+
|
|
28
|
+
Put nothing else under that heading: a prose line or another heading ends the section, and
|
|
29
|
+
criteria written after it are not read.
|
|
30
|
+
{% endblock %}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{% extends "_base.md.j2" %}
|
|
2
|
+
{% block body %}
|
|
3
|
+
You are the Implementer, reworking this task after a verdict requested changes.
|
|
4
|
+
|
|
5
|
+
Address every issue raised in the verdict above, commit your work, run the project's tests
|
|
6
|
+
and keep the trackers current. Report `completed` only when nothing remains; report
|
|
7
|
+
`needs_decision` or `blocked` with a concrete `question` otherwise.
|
|
8
|
+
|
|
9
|
+
Before committing, check the target project's instructions and documentation for its commit-message
|
|
10
|
+
convention. Follow that convention when one is documented; use Conventional Commits if none is
|
|
11
|
+
documented. Keep each subject concise and summarizing its commit. End every commit message with the
|
|
12
|
+
trailer `Task: {{ task_id }}` on its own line.
|
|
13
|
+
{% endblock %}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{% extends "_base.md.j2" %}
|
|
2
|
+
{% block body %}
|
|
3
|
+
You are the Leader triaging a task, before any work starts.
|
|
4
|
+
|
|
5
|
+
This turn is read-only: make no edits to the repository or the trackers.
|
|
6
|
+
|
|
7
|
+
Decide:
|
|
8
|
+
- `mode`: `implement` for a self-contained change, `plan` when a plan turn should shape the
|
|
9
|
+
work first, including when the request needs decomposing into several tasks, as it does
|
|
10
|
+
when it spans several tracker entries: each entry becomes its own task. An open
|
|
11
|
+
decision is not a reason to plan: a plan turn cannot raise it, while an implement task
|
|
12
|
+
raises it through consult to you and, if you cannot decide it, to the Operator.
|
|
13
|
+
- `complexity`, `acceptance_criteria` and `references`.
|
|
14
|
+
|
|
15
|
+
When the request has an acceptance-criteria section, carry its bullets into
|
|
16
|
+
`acceptance_criteria` unchanged. The section starts at a line holding only one of
|
|
17
|
+
`## {{ acceptance_criteria_heading }}`, `{{ acceptance_criteria_heading }}:`,
|
|
18
|
+
`**{{ acceptance_criteria_heading }}**` or `**{{ acceptance_criteria_heading }}:**`, in any
|
|
19
|
+
letter case.
|
|
20
|
+
{% endblock %}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{% extends "_base.md.j2" %}
|
|
2
|
+
{% import "_macros.md.j2" as macros %}
|
|
3
|
+
{% block body %}
|
|
4
|
+
You are the Leader verifying the Implementer's work on this task.
|
|
5
|
+
|
|
6
|
+
{{ macros.facts_section(facts) }}
|
|
7
|
+
|
|
8
|
+
Review the change with `git diff {{ base }}...HEAD` in the workspace. The facts above outrank
|
|
9
|
+
the Implementer's claims. Do not change code or tests: when something is missing or wrong,
|
|
10
|
+
choose `changes_requested` with the issues. The only files you may edit are the trackers, for
|
|
11
|
+
scope changes, and you commit those edits. Accept only when no issues remain;
|
|
12
|
+
non-blocking residual risks go into `residual_risks` or `new_tasks`, not `issues`.
|
|
13
|
+
|
|
14
|
+
If you commit tracker scope changes, follow the target project's documented commit-message
|
|
15
|
+
convention, using Conventional Commits if none is documented. Summarize the changes and end the
|
|
16
|
+
message with the trailer `Task: {{ task_id }}` on its own line.
|
|
17
|
+
|
|
18
|
+
Check the target project's instructions and documentation for its commit-message convention. When
|
|
19
|
+
you `accept`, `commit_subject` is a concise subject for the whole change following that convention,
|
|
20
|
+
or Conventional Commits if none is documented (no task reference). Keep `summary` short and describe
|
|
21
|
+
the entire change as it will land, drawing on every implementation and rework turn in the reports
|
|
22
|
+
above, not only this review.
|
|
23
|
+
{% if final_round %}
|
|
24
|
+
This is the final round: choose `accept`, `supersede` (decompose into `new_tasks`) or
|
|
25
|
+
`escalate`. `changes_requested` is not available this round.
|
|
26
|
+
{% endif %}
|
|
27
|
+
When you `escalate`, give the Operator options, exactly one `recommended`, and set `needs_input` on
|
|
28
|
+
an option that asks the Operator to provide something (a file, a result, access, a value).
|
|
29
|
+
{% endblock %}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""SQLite state store: schema, migrations, per-aggregate stores and the read-only reader."""
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""Operator command queue: append, dispatch and acknowledge."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from pydantic import TypeAdapter
|
|
9
|
+
|
|
10
|
+
from shikiru.domain.commands import (
|
|
11
|
+
COMMAND_ID_KEY,
|
|
12
|
+
Command,
|
|
13
|
+
CommandStatus,
|
|
14
|
+
IdentifiedCommand,
|
|
15
|
+
Submit,
|
|
16
|
+
)
|
|
17
|
+
from shikiru.store.database import (
|
|
18
|
+
dump_json,
|
|
19
|
+
format_timestamp,
|
|
20
|
+
load_json,
|
|
21
|
+
next_sequence,
|
|
22
|
+
require_after_insert,
|
|
23
|
+
transaction,
|
|
24
|
+
)
|
|
25
|
+
from shikiru.store.tasks import TASK_ID_PREFIX, TASK_SEQUENCE
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
import sqlite3 # noqa: TID251
|
|
29
|
+
|
|
30
|
+
from shikiru.clock import Clock
|
|
31
|
+
|
|
32
|
+
_COMMAND_ADAPTER: TypeAdapter[Command] = TypeAdapter(Command)
|
|
33
|
+
_RESULT_ADAPTER: TypeAdapter[dict[str, Any] | None] = TypeAdapter(dict[str, Any] | None)
|
|
34
|
+
_SUBMIT_TASK_ID_JSON_PATH = "$.task_id"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _identified(command: Command, command_id: int) -> Command:
|
|
38
|
+
if isinstance(command, IdentifiedCommand):
|
|
39
|
+
return command.model_copy(update={COMMAND_ID_KEY: command_id})
|
|
40
|
+
return command
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True, slots=True)
|
|
44
|
+
class QueuedCommand:
|
|
45
|
+
"""One pending command, as `CommandQueue.pending()` hands it to the engine."""
|
|
46
|
+
|
|
47
|
+
id: int
|
|
48
|
+
command: Command
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True, slots=True)
|
|
52
|
+
class CommandOutcome:
|
|
53
|
+
"""A command's lifecycle status, for `OperatorCommands` to wait on its ack."""
|
|
54
|
+
|
|
55
|
+
id: int
|
|
56
|
+
status: CommandStatus
|
|
57
|
+
result: dict[str, Any] | None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True, slots=True)
|
|
61
|
+
class StoredCommand:
|
|
62
|
+
"""One durable command row read for bounded operator history."""
|
|
63
|
+
|
|
64
|
+
id: int
|
|
65
|
+
ts: str
|
|
66
|
+
command: Command
|
|
67
|
+
status: CommandStatus
|
|
68
|
+
result: dict[str, Any] | None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(frozen=True, slots=True)
|
|
72
|
+
class CommandHistoryPage:
|
|
73
|
+
"""One bounded page of durable command rows."""
|
|
74
|
+
|
|
75
|
+
entries: list[StoredCommand]
|
|
76
|
+
offset: int
|
|
77
|
+
limit: int
|
|
78
|
+
total_count: int
|
|
79
|
+
has_more: bool
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def fetch_command_history(
|
|
83
|
+
connection: sqlite3.Connection,
|
|
84
|
+
*,
|
|
85
|
+
limit: int,
|
|
86
|
+
offset: int = 0,
|
|
87
|
+
status: CommandStatus | None = None,
|
|
88
|
+
) -> CommandHistoryPage:
|
|
89
|
+
"""Read one bounded newest-first page with its stored status and result."""
|
|
90
|
+
if status is None:
|
|
91
|
+
total_count = connection.execute("SELECT COUNT(*) FROM commands").fetchone()[0]
|
|
92
|
+
rows = connection.execute(
|
|
93
|
+
"SELECT id, ts, payload, status, result FROM commands "
|
|
94
|
+
"ORDER BY id DESC LIMIT ? OFFSET ?",
|
|
95
|
+
(limit, offset),
|
|
96
|
+
).fetchall()
|
|
97
|
+
else:
|
|
98
|
+
total_count = connection.execute(
|
|
99
|
+
"SELECT COUNT(*) FROM commands WHERE status = ?", (status.value,)
|
|
100
|
+
).fetchone()[0]
|
|
101
|
+
rows = connection.execute(
|
|
102
|
+
"SELECT id, ts, payload, status, result FROM commands WHERE status = ? "
|
|
103
|
+
"ORDER BY id DESC LIMIT ? OFFSET ?",
|
|
104
|
+
(status.value, limit, offset),
|
|
105
|
+
).fetchall()
|
|
106
|
+
entries = [_stored_command(row) for row in rows]
|
|
107
|
+
return CommandHistoryPage(
|
|
108
|
+
entries=entries,
|
|
109
|
+
offset=offset,
|
|
110
|
+
limit=limit,
|
|
111
|
+
total_count=total_count,
|
|
112
|
+
has_more=offset + len(entries) < total_count,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def fetch_command(
|
|
117
|
+
connection: sqlite3.Connection,
|
|
118
|
+
command_id: int,
|
|
119
|
+
) -> StoredCommand | None:
|
|
120
|
+
"""Read one durable command status by id without changing its lifecycle."""
|
|
121
|
+
row = connection.execute(
|
|
122
|
+
"SELECT id, ts, payload, status, result FROM commands WHERE id = ?",
|
|
123
|
+
(command_id,),
|
|
124
|
+
).fetchone()
|
|
125
|
+
return _stored_command(row) if row is not None else None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def fetch_pending_command(
|
|
129
|
+
connection: sqlite3.Connection,
|
|
130
|
+
command: Command,
|
|
131
|
+
) -> StoredCommand | None:
|
|
132
|
+
"""Find an identical still-pending command without scanning history into memory."""
|
|
133
|
+
payload = dump_json(_COMMAND_ADAPTER, command)
|
|
134
|
+
if isinstance(command, Submit):
|
|
135
|
+
row = connection.execute(
|
|
136
|
+
"SELECT id, ts, payload, status, result FROM commands "
|
|
137
|
+
"WHERE status = ? AND kind = ? AND "
|
|
138
|
+
"json_remove(payload, ?) = json_remove(?, ?) ORDER BY id DESC LIMIT 1",
|
|
139
|
+
(
|
|
140
|
+
CommandStatus.PENDING.value,
|
|
141
|
+
command.kind.value,
|
|
142
|
+
_SUBMIT_TASK_ID_JSON_PATH,
|
|
143
|
+
payload,
|
|
144
|
+
_SUBMIT_TASK_ID_JSON_PATH,
|
|
145
|
+
),
|
|
146
|
+
).fetchone()
|
|
147
|
+
else:
|
|
148
|
+
row = connection.execute(
|
|
149
|
+
"SELECT id, ts, payload, status, result FROM commands "
|
|
150
|
+
"WHERE status = ? AND kind = ? AND payload = ? ORDER BY id DESC LIMIT 1",
|
|
151
|
+
(CommandStatus.PENDING.value, command.kind.value, payload),
|
|
152
|
+
).fetchone()
|
|
153
|
+
return _stored_command(row) if row is not None else None
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _stored_command(row: sqlite3.Row) -> StoredCommand:
|
|
157
|
+
result = row["result"]
|
|
158
|
+
return StoredCommand(
|
|
159
|
+
id=row["id"],
|
|
160
|
+
ts=row["ts"],
|
|
161
|
+
command=load_json(_COMMAND_ADAPTER, row["payload"]),
|
|
162
|
+
status=CommandStatus(row["status"]),
|
|
163
|
+
result=load_json(_RESULT_ADAPTER, result) if result is not None else None,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class CommandQueue:
|
|
168
|
+
"""Appends operator commands and lets the engine dispatch and acknowledge them."""
|
|
169
|
+
|
|
170
|
+
def __init__(self, connection: sqlite3.Connection, clock: Clock) -> None:
|
|
171
|
+
"""Use `connection` and `clock` for every write below.
|
|
172
|
+
|
|
173
|
+
Unlike the other stores, this connection isn't only the engine's: clients (CLI, MCP)
|
|
174
|
+
append commands and, for `Submit`, allocate the task id here too.
|
|
175
|
+
"""
|
|
176
|
+
self._connection = connection
|
|
177
|
+
self._clock = clock
|
|
178
|
+
|
|
179
|
+
def append(self, command: Command) -> QueuedCommand:
|
|
180
|
+
"""Append `command`; a `Submit` is given its `T-n` id here, atomically."""
|
|
181
|
+
ts = format_timestamp(self._clock.now())
|
|
182
|
+
with transaction(self._connection):
|
|
183
|
+
if isinstance(command, Submit):
|
|
184
|
+
seq = next_sequence(self._connection, TASK_SEQUENCE)
|
|
185
|
+
command = command.model_copy(update={"task_id": f"{TASK_ID_PREFIX}{seq}"})
|
|
186
|
+
cursor = self._connection.execute(
|
|
187
|
+
"INSERT INTO commands (ts, kind, payload) VALUES (?, ?, ?)",
|
|
188
|
+
(ts, command.kind.value, dump_json(_COMMAND_ADAPTER, command)),
|
|
189
|
+
)
|
|
190
|
+
command_id = cursor.lastrowid
|
|
191
|
+
command_id = require_after_insert(
|
|
192
|
+
command_id, "sqlite did not allocate a rowid for the new command"
|
|
193
|
+
)
|
|
194
|
+
return QueuedCommand(id=command_id, command=command)
|
|
195
|
+
|
|
196
|
+
def pending(self) -> list[QueuedCommand]:
|
|
197
|
+
"""Read every pending command, oldest first, as typed `domain.commands.Command` models."""
|
|
198
|
+
rows = self._connection.execute(
|
|
199
|
+
"SELECT id, payload FROM commands WHERE status = ? ORDER BY id",
|
|
200
|
+
(CommandStatus.PENDING.value,),
|
|
201
|
+
).fetchall()
|
|
202
|
+
return [
|
|
203
|
+
QueuedCommand(
|
|
204
|
+
id=row["id"],
|
|
205
|
+
command=_identified(load_json(_COMMAND_ADAPTER, row["payload"]), row["id"]),
|
|
206
|
+
)
|
|
207
|
+
for row in rows
|
|
208
|
+
]
|
|
209
|
+
|
|
210
|
+
def mark_done(self, command_id: int, result: dict[str, Any] | None = None) -> None:
|
|
211
|
+
"""Mark `command_id` done, recording `result` if the client needs it."""
|
|
212
|
+
self._set_status(command_id, CommandStatus.DONE, result)
|
|
213
|
+
|
|
214
|
+
def mark_rejected(self, command_id: int, result: dict[str, Any] | None = None) -> None:
|
|
215
|
+
"""Mark `command_id` rejected, recording `result` (e.g. an error message)."""
|
|
216
|
+
self._set_status(command_id, CommandStatus.REJECTED, result)
|
|
217
|
+
|
|
218
|
+
def _set_status(
|
|
219
|
+
self, command_id: int, status: CommandStatus, result: dict[str, Any] | None
|
|
220
|
+
) -> None:
|
|
221
|
+
self._connection.execute(
|
|
222
|
+
"UPDATE commands SET status = ?, result = ? WHERE id = ?",
|
|
223
|
+
(
|
|
224
|
+
status.value,
|
|
225
|
+
dump_json(_RESULT_ADAPTER, result),
|
|
226
|
+
command_id,
|
|
227
|
+
),
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
def status(self, command_id: int) -> CommandOutcome | None:
|
|
231
|
+
"""Read `command_id`'s current lifecycle status, or `None` if it doesn't exist."""
|
|
232
|
+
row = self._connection.execute(
|
|
233
|
+
"SELECT id, status, result FROM commands WHERE id = ?", (command_id,)
|
|
234
|
+
).fetchone()
|
|
235
|
+
if row is None:
|
|
236
|
+
return None
|
|
237
|
+
result = row["result"]
|
|
238
|
+
return CommandOutcome(
|
|
239
|
+
id=row["id"],
|
|
240
|
+
status=CommandStatus(row["status"]),
|
|
241
|
+
result=load_json(_RESULT_ADAPTER, result) if result is not None else None,
|
|
242
|
+
)
|