@event4u/agent-config 1.13.0 → 1.14.0
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.
- package/.agent-src/commands/agent-handoff.md +3 -0
- package/.agent-src/commands/agent-status.md +3 -0
- package/.agent-src/commands/agents-audit.md +4 -0
- package/.agent-src/commands/agents-cleanup.md +6 -1
- package/.agent-src/commands/agents-prepare.md +3 -0
- package/.agent-src/commands/analyze-reference-repo.md +4 -0
- package/.agent-src/commands/bug-fix.md +5 -1
- package/.agent-src/commands/bug-investigate.md +4 -0
- package/.agent-src/commands/chat-history-checkpoint.md +126 -0
- package/.agent-src/commands/chat-history-clear.md +5 -0
- package/.agent-src/commands/chat-history-resume.md +5 -0
- package/.agent-src/commands/chat-history.md +5 -0
- package/.agent-src/commands/check-current-md.md +126 -0
- package/.agent-src/commands/commit-in-chunks.md +98 -0
- package/.agent-src/commands/commit.md +4 -0
- package/.agent-src/commands/compress.md +3 -0
- package/.agent-src/commands/context-create.md +4 -0
- package/.agent-src/commands/context-refactor.md +4 -0
- package/.agent-src/commands/copilot-agents-init.md +3 -0
- package/.agent-src/commands/copilot-agents-optimize.md +3 -0
- package/.agent-src/commands/create-pr-description.md +4 -0
- package/.agent-src/commands/create-pr.md +4 -0
- package/.agent-src/commands/do-and-judge.md +4 -1
- package/.agent-src/commands/do-in-steps.md +3 -0
- package/.agent-src/commands/e2e-heal.md +4 -0
- package/.agent-src/commands/e2e-plan.md +4 -0
- package/.agent-src/commands/estimate-ticket.md +4 -1
- package/.agent-src/commands/feature-dev.md +4 -0
- package/.agent-src/commands/feature-explore.md +4 -0
- package/.agent-src/commands/feature-plan.md +4 -0
- package/.agent-src/commands/feature-refactor.md +4 -0
- package/.agent-src/commands/feature-roadmap.md +6 -0
- package/.agent-src/commands/fix-ci.md +4 -0
- package/.agent-src/commands/fix-portability.md +3 -0
- package/.agent-src/commands/fix-pr-bot-comments.md +4 -0
- package/.agent-src/commands/fix-pr-comments.md +4 -0
- package/.agent-src/commands/fix-pr-developer-comments.md +4 -0
- package/.agent-src/commands/fix-references.md +3 -0
- package/.agent-src/commands/fix-seeder.md +4 -0
- package/.agent-src/commands/implement-ticket.md +39 -13
- package/.agent-src/commands/jira-ticket.md +4 -0
- package/.agent-src/commands/judge.md +3 -0
- package/.agent-src/commands/memory-add.md +5 -3
- package/.agent-src/commands/memory-full.md +5 -2
- package/.agent-src/commands/memory-promote.md +7 -6
- package/.agent-src/commands/mode.md +3 -0
- package/.agent-src/commands/module-create.md +4 -0
- package/.agent-src/commands/module-explore.md +4 -0
- package/.agent-src/commands/onboard.md +24 -0
- package/.agent-src/commands/optimize-agents.md +4 -0
- package/.agent-src/commands/optimize-augmentignore.md +3 -0
- package/.agent-src/commands/optimize-rtk-filters.md +3 -0
- package/.agent-src/commands/optimize-skills.md +4 -0
- package/.agent-src/commands/override-create.md +4 -0
- package/.agent-src/commands/override-manage.md +4 -0
- package/.agent-src/commands/package-reset.md +3 -0
- package/.agent-src/commands/package-test.md +3 -0
- package/.agent-src/commands/prepare-for-review.md +4 -0
- package/.agent-src/commands/project-analyze.md +4 -0
- package/.agent-src/commands/project-health.md +4 -0
- package/.agent-src/commands/propose-memory.md +6 -8
- package/.agent-src/commands/quality-fix.md +4 -0
- package/.agent-src/commands/refine-ticket.md +4 -1
- package/.agent-src/commands/review-changes.md +4 -0
- package/.agent-src/commands/review-routing.md +4 -0
- package/.agent-src/commands/roadmap-create.md +7 -0
- package/.agent-src/commands/roadmap-execute.md +12 -1
- package/.agent-src/commands/rule-compliance-audit.md +4 -0
- package/.agent-src/commands/set-cost-profile.md +3 -0
- package/.agent-src/commands/sync-agent-settings.md +3 -0
- package/.agent-src/commands/sync-gitignore.md +3 -0
- package/.agent-src/commands/tests-create.md +4 -0
- package/.agent-src/commands/tests-execute.md +4 -0
- package/.agent-src/commands/threat-model.md +4 -0
- package/.agent-src/commands/update-form-request-messages.md +4 -0
- package/.agent-src/commands/upstream-contribute.md +4 -0
- package/.agent-src/commands/work.md +161 -0
- package/.agent-src/guidelines/agent-infra/engineering-memory-data-format.md +2 -6
- package/.agent-src/guidelines/agent-infra/layered-settings.md +0 -1
- package/.agent-src/guidelines/agent-infra/memory-access.md +0 -7
- package/.agent-src/guidelines/agent-infra/role-contracts.md +2 -4
- package/.agent-src/guidelines/agent-infra/self-improvement-pipeline.md +0 -1
- package/.agent-src/guidelines/php/patterns/strategy.md +180 -2
- package/.agent-src/personas/README.md +0 -1
- package/.agent-src/rules/artifact-drafting-protocol.md +7 -2
- package/.agent-src/rules/artifact-engagement-recording.md +133 -0
- package/.agent-src/rules/ask-when-uncertain.md +18 -13
- package/.agent-src/rules/augment-portability.md +8 -0
- package/.agent-src/rules/autonomous-execution.md +158 -0
- package/.agent-src/rules/chat-history.md +147 -118
- package/.agent-src/rules/cli-output-handling.md +26 -3
- package/.agent-src/rules/command-suggestion.md +133 -0
- package/.agent-src/rules/commit-policy.md +99 -0
- package/.agent-src/rules/direct-answers.md +114 -0
- package/.agent-src/rules/docs-sync.md +36 -0
- package/.agent-src/rules/downstream-changes.md +10 -9
- package/.agent-src/rules/improve-before-implement.md +9 -6
- package/.agent-src/rules/language-and-tone.md +81 -6
- package/.agent-src/rules/non-destructive-by-default.md +117 -0
- package/.agent-src/rules/package-ci-checks.md +4 -0
- package/.agent-src/rules/preservation-guard.md +20 -0
- package/.agent-src/rules/roadmap-progress-sync.md +103 -30
- package/.agent-src/rules/scope-control.md +42 -1
- package/.agent-src/rules/size-enforcement.md +1 -3
- package/.agent-src/rules/skill-quality.md +3 -8
- package/.agent-src/rules/ui-audit-before-build.md +106 -0
- package/.agent-src/rules/user-interaction.md +82 -50
- package/.agent-src/scripts/update_roadmap_progress.py +17 -5
- package/.agent-src/skills/blade-ui/SKILL.md +30 -5
- package/.agent-src/skills/command-routing/SKILL.md +32 -0
- package/.agent-src/skills/command-writing/SKILL.md +41 -2
- package/.agent-src/skills/description-assist/SKILL.md +21 -0
- package/.agent-src/skills/estimate-ticket/SKILL.md +0 -1
- package/.agent-src/skills/existing-ui-audit/SKILL.md +187 -0
- package/.agent-src/skills/fe-design/SKILL.md +72 -60
- package/.agent-src/skills/finishing-a-development-branch/SKILL.md +4 -0
- package/.agent-src/skills/flux/SKILL.md +31 -4
- package/.agent-src/skills/guideline-writing/SKILL.md +24 -2
- package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +51 -9
- package/.agent-src/skills/livewire/SKILL.md +30 -4
- package/.agent-src/skills/md-language-check/SKILL.md +103 -0
- package/.agent-src/skills/php-coder/SKILL.md +24 -0
- package/.agent-src/skills/react-shadcn-ui/SKILL.md +121 -0
- package/.agent-src/skills/refine-prompt/SKILL.md +220 -0
- package/.agent-src/skills/refine-ticket/SKILL.md +2 -4
- package/.agent-src/skills/roadmap-management/SKILL.md +10 -3
- package/.agent-src/skills/rule-writing/SKILL.md +23 -1
- package/.agent-src/skills/skill-writing/SKILL.md +1 -3
- package/.agent-src/skills/upstream-contribute/SKILL.md +1 -1
- package/.agent-src/skills/using-git-worktrees/SKILL.md +3 -1
- package/.agent-src/templates/AGENTS.md +24 -6
- package/.agent-src/templates/agent-settings.md +149 -0
- package/.agent-src/templates/roadmaps.md +8 -2
- package/.agent-src/templates/scripts/implement_ticket/__init__.py +63 -26
- package/.agent-src/templates/scripts/implement_ticket/__main__.py +8 -2
- package/.agent-src/templates/scripts/telemetry/__init__.py +42 -0
- package/.agent-src/templates/scripts/telemetry/aggregator.py +154 -0
- package/.agent-src/templates/scripts/telemetry/boundary.py +171 -0
- package/.agent-src/templates/scripts/telemetry/engagement.py +238 -0
- package/.agent-src/templates/scripts/telemetry/report_renderer.py +170 -0
- package/.agent-src/templates/scripts/telemetry/settings.py +112 -0
- package/.agent-src/templates/scripts/telemetry_record.py +166 -0
- package/.agent-src/templates/scripts/telemetry_report.py +161 -0
- package/.agent-src/templates/scripts/telemetry_status.py +142 -0
- package/.agent-src/templates/scripts/work_engine/__init__.py +58 -0
- package/.agent-src/templates/scripts/work_engine/__main__.py +9 -0
- package/.agent-src/templates/scripts/work_engine/cli.py +592 -0
- package/.agent-src/templates/scripts/{implement_ticket → work_engine}/delivery_state.py +7 -0
- package/.agent-src/templates/scripts/work_engine/directives/__init__.py +33 -0
- package/.agent-src/templates/scripts/work_engine/directives/backend/__init__.py +98 -0
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/analyze.py +1 -1
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/implement.py +2 -2
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/memory.py +1 -1
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/plan.py +1 -1
- package/.agent-src/templates/scripts/work_engine/directives/backend/refine.py +396 -0
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/report.py +36 -4
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/test.py +2 -2
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/verify.py +2 -2
- package/.agent-src/templates/scripts/work_engine/directives/mixed/__init__.py +116 -0
- package/.agent-src/templates/scripts/work_engine/directives/mixed/contract.py +254 -0
- package/.agent-src/templates/scripts/work_engine/directives/mixed/stitch.py +229 -0
- package/.agent-src/templates/scripts/work_engine/directives/mixed/ui.py +231 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/__init__.py +113 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/_passthrough.py +44 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/apply.py +241 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/audit.py +414 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/design.py +335 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/polish.py +510 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/review.py +468 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/__init__.py +119 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/_skipped.py +37 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/apply.py +165 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/refine.py +66 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/report.py +62 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/test.py +115 -0
- package/.agent-src/templates/scripts/work_engine/dispatcher.py +331 -0
- package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +54 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +32 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +103 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +44 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +42 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_heartbeat.py +50 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_turn_check.py +49 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/directive_set_guard.py +53 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/halt_surface_audit.py +50 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/state_shape_validation.py +52 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/trace.py +84 -0
- package/.agent-src/templates/scripts/work_engine/hooks/context.py +66 -0
- package/.agent-src/templates/scripts/work_engine/hooks/events.py +44 -0
- package/.agent-src/templates/scripts/work_engine/hooks/exceptions.py +79 -0
- package/.agent-src/templates/scripts/work_engine/hooks/registry.py +60 -0
- package/.agent-src/templates/scripts/work_engine/hooks/runner.py +73 -0
- package/.agent-src/templates/scripts/work_engine/hooks/settings.py +141 -0
- package/.agent-src/templates/scripts/work_engine/intent/__init__.py +47 -0
- package/.agent-src/templates/scripts/work_engine/intent/classify.py +280 -0
- package/.agent-src/templates/scripts/work_engine/migration/__init__.py +8 -0
- package/.agent-src/templates/scripts/work_engine/migration/v0_to_v1.py +199 -0
- package/.agent-src/templates/scripts/work_engine/resolvers/__init__.py +22 -0
- package/.agent-src/templates/scripts/work_engine/resolvers/diff.py +106 -0
- package/.agent-src/templates/scripts/work_engine/resolvers/file.py +113 -0
- package/.agent-src/templates/scripts/work_engine/resolvers/prompt.py +90 -0
- package/.agent-src/templates/scripts/work_engine/scoring/__init__.py +14 -0
- package/.agent-src/templates/scripts/work_engine/scoring/confidence.py +300 -0
- package/.agent-src/templates/scripts/work_engine/stack/__init__.py +31 -0
- package/.agent-src/templates/scripts/work_engine/stack/detect.py +187 -0
- package/.agent-src/templates/scripts/work_engine/state.py +641 -0
- package/.claude-plugin/marketplace.json +105 -2
- package/AGENTS.md +36 -8
- package/CHANGELOG.md +534 -0
- package/README.md +125 -4
- package/config/agent-settings.template.yml +45 -0
- package/config/gitignore-block.txt +4 -0
- package/docs/architecture.md +28 -1
- package/docs/development.md +1 -1
- package/docs/getting-started.md +2 -2
- package/docs/installation.md +86 -0
- package/docs/showcase.md +204 -0
- package/package.json +1 -1
- package/scripts/agent-config +199 -0
- package/scripts/audit_cloud_compatibility.py +288 -0
- package/scripts/build_cloud_bundle.py +458 -0
- package/scripts/build_linear_digest.py +263 -0
- package/scripts/chat_history.py +796 -7
- package/scripts/check_compression.py +139 -0
- package/scripts/check_iron_law_prominence.py +143 -0
- package/scripts/check_md_language.py +159 -0
- package/scripts/check_portability.py +36 -0
- package/scripts/check_reply_consistency.py +140 -0
- package/scripts/command_suggester/__init__.py +51 -0
- package/scripts/command_suggester/cooldown.py +132 -0
- package/scripts/command_suggester/loader.py +70 -0
- package/scripts/command_suggester/match.py +180 -0
- package/scripts/command_suggester/rank.py +120 -0
- package/scripts/command_suggester/render.py +86 -0
- package/scripts/command_suggester/sanitize.py +113 -0
- package/scripts/command_suggester/settings.py +125 -0
- package/scripts/command_suggester/types.py +78 -0
- package/scripts/hooks/augment-chat-history.sh +56 -0
- package/scripts/install-hooks.sh +67 -0
- package/scripts/install.py +150 -33
- package/scripts/lint_marketplace.py +27 -0
- package/scripts/migrate_command_suggestions.py +151 -0
- package/scripts/schemas/command.schema.json +41 -0
- package/scripts/skill_linter.py +67 -0
- package/scripts/sync_agent_settings.py +42 -12
- package/templates/consumer-settings/augment-cli-hooks.json +54 -0
- package/templates/consumer-settings/claude-settings.json +55 -1
- package/.agent-src/templates/scripts/implement_ticket/cli.py +0 -171
- package/.agent-src/templates/scripts/implement_ticket/dispatcher.py +0 -134
- package/.agent-src/templates/scripts/implement_ticket/steps/__init__.py +0 -49
- package/.agent-src/templates/scripts/implement_ticket/steps/refine.py +0 -140
- /package/.agent-src/templates/scripts/{implement_ticket → work_engine}/persona_policy.py +0 -0
|
@@ -1,40 +1,77 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
``agents/contexts/implement-ticket-flow.md``):
|
|
12
|
-
|
|
13
|
-
- Runtime is Python 3.10+.
|
|
14
|
-
- The dispatcher is linear, not a DAG. Eight fixed steps, fixed order.
|
|
15
|
-
- ``DeliveryState`` is the only object shared between steps — no
|
|
16
|
-
hidden state, no side channels.
|
|
17
|
-
- Every step terminates in ``success | blocked | partial``. ``blocked``
|
|
18
|
-
and ``partial`` halt the flow and surface numbered questions.
|
|
19
|
-
- The dispatcher never calls git, writes commits, or opens PRs.
|
|
1
|
+
"""``implement_ticket`` — deprecated shim, retained for one release.
|
|
2
|
+
|
|
3
|
+
The engine moved to :mod:`work_engine` in R1 Phase 3. This module
|
|
4
|
+
re-exports the public surface so existing imports keep working, but
|
|
5
|
+
emits :class:`DeprecationWarning` on import. The Golden-Transcript
|
|
6
|
+
freeze-guard pins ``./agent-config implement-ticket`` against the
|
|
7
|
+
locked baseline; the shim keeps that path byte-stable while internal
|
|
8
|
+
callers migrate to ``work_engine``.
|
|
9
|
+
|
|
10
|
+
Remove after the next public release of this package.
|
|
20
11
|
"""
|
|
21
12
|
from __future__ import annotations
|
|
22
13
|
|
|
23
|
-
|
|
14
|
+
import sys as _sys
|
|
15
|
+
import warnings as _warnings
|
|
16
|
+
|
|
17
|
+
_warnings.warn(
|
|
18
|
+
"implement_ticket has moved to work_engine; importing implement_ticket "
|
|
19
|
+
"is deprecated and will be removed in a future release. Update imports "
|
|
20
|
+
"to `from work_engine import …`.",
|
|
21
|
+
DeprecationWarning,
|
|
22
|
+
stacklevel=2,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Register submodule aliases so `from implement_ticket.steps.plan import …`
|
|
26
|
+
# and friends keep resolving to the work_engine implementation. The legacy
|
|
27
|
+
# package no longer ships these submodules on disk; sys.modules entries
|
|
28
|
+
# keep dotted-path imports working until the shim is removed.
|
|
29
|
+
import work_engine as _we_pkg # noqa: E402
|
|
30
|
+
import work_engine.cli as _we_cli # noqa: E402
|
|
31
|
+
import work_engine.delivery_state as _we_delivery_state # noqa: E402
|
|
32
|
+
import work_engine.dispatcher as _we_dispatcher # noqa: E402
|
|
33
|
+
import work_engine.persona_policy as _we_persona_policy # noqa: E402
|
|
34
|
+
import work_engine.directives.backend as _we_steps # noqa: E402
|
|
35
|
+
from work_engine.directives.backend import ( # noqa: E402
|
|
36
|
+
analyze as _we_step_analyze,
|
|
37
|
+
implement as _we_step_implement,
|
|
38
|
+
memory as _we_step_memory,
|
|
39
|
+
plan as _we_step_plan,
|
|
40
|
+
refine as _we_step_refine,
|
|
41
|
+
report as _we_step_report,
|
|
42
|
+
test as _we_step_test,
|
|
43
|
+
verify as _we_step_verify,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
_sys.modules.setdefault("implement_ticket.cli", _we_cli)
|
|
47
|
+
_sys.modules.setdefault("implement_ticket.delivery_state", _we_delivery_state)
|
|
48
|
+
_sys.modules.setdefault("implement_ticket.dispatcher", _we_dispatcher)
|
|
49
|
+
_sys.modules.setdefault("implement_ticket.persona_policy", _we_persona_policy)
|
|
50
|
+
_sys.modules.setdefault("implement_ticket.steps", _we_steps)
|
|
51
|
+
_sys.modules.setdefault("implement_ticket.steps.analyze", _we_step_analyze)
|
|
52
|
+
_sys.modules.setdefault("implement_ticket.steps.implement", _we_step_implement)
|
|
53
|
+
_sys.modules.setdefault("implement_ticket.steps.memory", _we_step_memory)
|
|
54
|
+
_sys.modules.setdefault("implement_ticket.steps.plan", _we_step_plan)
|
|
55
|
+
_sys.modules.setdefault("implement_ticket.steps.refine", _we_step_refine)
|
|
56
|
+
_sys.modules.setdefault("implement_ticket.steps.report", _we_step_report)
|
|
57
|
+
_sys.modules.setdefault("implement_ticket.steps.test", _we_step_test)
|
|
58
|
+
_sys.modules.setdefault("implement_ticket.steps.verify", _we_step_verify)
|
|
59
|
+
|
|
60
|
+
from work_engine import ( # noqa: E402,F401 — re-export for backwards compat
|
|
24
61
|
AGENT_DIRECTIVE_PREFIX,
|
|
62
|
+
DEFAULT_PERSONA,
|
|
63
|
+
DEFAULT_STATE_FILE,
|
|
25
64
|
DeliveryState,
|
|
26
65
|
Outcome,
|
|
66
|
+
PersonaPolicy,
|
|
67
|
+
STEP_ORDER,
|
|
27
68
|
Step,
|
|
28
69
|
StepResult,
|
|
29
70
|
agent_directive,
|
|
71
|
+
dispatch,
|
|
30
72
|
is_agent_directive,
|
|
31
|
-
)
|
|
32
|
-
from .cli import DEFAULT_STATE_FILE, main
|
|
33
|
-
from .dispatcher import STEP_ORDER, dispatch
|
|
34
|
-
from .persona_policy import (
|
|
35
|
-
DEFAULT_PERSONA,
|
|
36
|
-
PersonaPolicy,
|
|
37
73
|
known_personas,
|
|
74
|
+
main,
|
|
38
75
|
resolve_policy,
|
|
39
76
|
)
|
|
40
77
|
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Deprecated CLI entry point — delegates to :mod:`work_engine`.
|
|
2
|
+
|
|
3
|
+
``python3 -m implement_ticket`` still works because the Golden-Transcript
|
|
4
|
+
freeze-guard pins that invocation. Internally it forwards to
|
|
5
|
+
``work_engine.cli.main`` after emitting a ``DeprecationWarning`` from
|
|
6
|
+
the package ``__init__``.
|
|
7
|
+
"""
|
|
2
8
|
from __future__ import annotations
|
|
3
9
|
|
|
4
10
|
import sys
|
|
5
11
|
|
|
6
|
-
from .cli import main
|
|
12
|
+
from work_engine.cli import main
|
|
7
13
|
|
|
8
14
|
if __name__ == "__main__":
|
|
9
15
|
sys.exit(main())
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""``telemetry`` — artefact engagement recording (default-off).
|
|
2
|
+
|
|
3
|
+
The package owns the local-only engagement log
|
|
4
|
+
(``.agent-engagement.jsonl``) that records, at task boundaries, which
|
|
5
|
+
artefacts (skills, rules, commands, guidelines, personas) the agent
|
|
6
|
+
``consulted`` (loaded into context) and ``applied`` (cited or directly
|
|
7
|
+
drove a decision).
|
|
8
|
+
|
|
9
|
+
Architectural constraints (from
|
|
10
|
+
``agents/roadmaps/road-to-artifact-engagement-telemetry.md`` Phase 1):
|
|
11
|
+
|
|
12
|
+
- Default-off. ``telemetry.artifact_engagement.enabled: false`` in
|
|
13
|
+
``.agent-settings.yml`` produces zero file IO and zero token cost.
|
|
14
|
+
- Local only. No server-side aggregation, no cross-repo sync.
|
|
15
|
+
- ID-only payloads. No paths, no file contents, no prompts, no
|
|
16
|
+
secrets ever reach the log.
|
|
17
|
+
- Append-only JSONL. One event per task / phase-step boundary.
|
|
18
|
+
- Strict schema. Unknown artefact kinds are rejected.
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from .engagement import (
|
|
23
|
+
ALLOWED_BOUNDARY_KINDS,
|
|
24
|
+
ALLOWED_KINDS,
|
|
25
|
+
SCHEMA_VERSION,
|
|
26
|
+
EngagementEvent,
|
|
27
|
+
EngagementSchemaError,
|
|
28
|
+
append_event,
|
|
29
|
+
now_utc_iso,
|
|
30
|
+
parse_event,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"ALLOWED_BOUNDARY_KINDS",
|
|
35
|
+
"ALLOWED_KINDS",
|
|
36
|
+
"SCHEMA_VERSION",
|
|
37
|
+
"EngagementEvent",
|
|
38
|
+
"EngagementSchemaError",
|
|
39
|
+
"append_event",
|
|
40
|
+
"now_utc_iso",
|
|
41
|
+
"parse_event",
|
|
42
|
+
]
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""Engagement-log aggregator (Phase 4).
|
|
2
|
+
|
|
3
|
+
Pure-stdlib reader: streams ``.agent-engagement.jsonl``, groups events by
|
|
4
|
+
``(kind, id)``, and returns per-artefact statistics. The renderer in
|
|
5
|
+
``report_renderer.py`` consumes the dataclasses produced here.
|
|
6
|
+
|
|
7
|
+
Design contract:
|
|
8
|
+
|
|
9
|
+
- **Skip, don't crash.** Malformed JSONL lines are counted in
|
|
10
|
+
``AggregateResult.skipped_lines`` and dropped. Phase 4 Step 4 locks
|
|
11
|
+
this behaviour: a single corrupt line in a 10k-line log must not
|
|
12
|
+
block the report.
|
|
13
|
+
- **No IO besides the log read.** No network, no settings reads, no
|
|
14
|
+
log creation. Caller (CLI) is responsible for feeding a real path.
|
|
15
|
+
- **``since`` is exclusive on the lower bound** — ``since`` of
|
|
16
|
+
``2026-04-01T00:00:00Z`` keeps events with ``ts > since``. ``None``
|
|
17
|
+
means "include everything".
|
|
18
|
+
- **Stats are sort-stable.** ``rank_artefacts`` returns a list ordered
|
|
19
|
+
by ``applied`` desc, ``consulted`` desc, then ``(kind, id)`` asc, so
|
|
20
|
+
two reports over the same log render byte-identical.
|
|
21
|
+
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from dataclasses import dataclass, field
|
|
25
|
+
from datetime import datetime, timezone
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Iterable, Iterator
|
|
28
|
+
|
|
29
|
+
from .engagement import EngagementEvent, EngagementSchemaError, parse_event
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class ArtefactStat:
|
|
34
|
+
kind: str
|
|
35
|
+
artefact_id: str
|
|
36
|
+
consulted: int
|
|
37
|
+
applied: int
|
|
38
|
+
last_seen_ts: str
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def applied_ratio(self) -> float:
|
|
42
|
+
"""Applied / consulted. ``0.0`` when never consulted (impossible
|
|
43
|
+
in practice — applied is a strict subset of consulted — but the
|
|
44
|
+
guard keeps the division safe for malformed inputs)."""
|
|
45
|
+
return (self.applied / self.consulted) if self.consulted else 0.0
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class AggregateResult:
|
|
50
|
+
total_events: int = 0
|
|
51
|
+
parsed_events: int = 0
|
|
52
|
+
skipped_lines: int = 0
|
|
53
|
+
earliest_ts: str | None = None
|
|
54
|
+
latest_ts: str | None = None
|
|
55
|
+
artefacts: dict[tuple[str, str], dict[str, object]] = field(default_factory=dict)
|
|
56
|
+
|
|
57
|
+
def stats(self) -> list[ArtefactStat]:
|
|
58
|
+
"""Materialise the accumulated buckets as immutable stats."""
|
|
59
|
+
out: list[ArtefactStat] = []
|
|
60
|
+
for (kind, art_id), bucket in self.artefacts.items():
|
|
61
|
+
out.append(
|
|
62
|
+
ArtefactStat(
|
|
63
|
+
kind=kind,
|
|
64
|
+
artefact_id=art_id,
|
|
65
|
+
consulted=int(bucket["consulted"]),
|
|
66
|
+
applied=int(bucket["applied"]),
|
|
67
|
+
last_seen_ts=str(bucket["last_seen_ts"]),
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
return out
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _parse_iso(ts: str) -> datetime | None:
|
|
74
|
+
"""Parse a ``%Y-%m-%dT%H:%M:%SZ`` stamp into UTC. Returns ``None``
|
|
75
|
+
for malformed stamps so the caller can skip the comparison cleanly.
|
|
76
|
+
"""
|
|
77
|
+
if not isinstance(ts, str) or not ts:
|
|
78
|
+
return None
|
|
79
|
+
try:
|
|
80
|
+
# strptime with literal Z handles the ``now_utc_iso`` format.
|
|
81
|
+
return datetime.strptime(ts, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
|
|
82
|
+
except ValueError:
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _iter_events(log_path: Path) -> Iterator[tuple[int, EngagementEvent | None]]:
|
|
87
|
+
"""Yield ``(line_number, event_or_None)``. ``None`` signals a skip."""
|
|
88
|
+
if not log_path.is_file():
|
|
89
|
+
return
|
|
90
|
+
with log_path.open("r", encoding="utf-8") as fh:
|
|
91
|
+
for line_no, line in enumerate(fh, start=1):
|
|
92
|
+
stripped = line.strip()
|
|
93
|
+
if not stripped:
|
|
94
|
+
continue
|
|
95
|
+
try:
|
|
96
|
+
event = parse_event(stripped + "\n")
|
|
97
|
+
except EngagementSchemaError:
|
|
98
|
+
yield line_no, None
|
|
99
|
+
continue
|
|
100
|
+
yield line_no, event
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def aggregate(
|
|
104
|
+
log_path: Path,
|
|
105
|
+
*,
|
|
106
|
+
since: datetime | None = None,
|
|
107
|
+
) -> AggregateResult:
|
|
108
|
+
"""Stream the JSONL log and compute per-artefact stats."""
|
|
109
|
+
result = AggregateResult()
|
|
110
|
+
for _line_no, event in _iter_events(log_path):
|
|
111
|
+
result.total_events += 1
|
|
112
|
+
if event is None:
|
|
113
|
+
result.skipped_lines += 1
|
|
114
|
+
continue
|
|
115
|
+
ts = _parse_iso(event.ts)
|
|
116
|
+
if since is not None and ts is not None and ts <= since:
|
|
117
|
+
continue
|
|
118
|
+
result.parsed_events += 1
|
|
119
|
+
if result.earliest_ts is None or event.ts < result.earliest_ts:
|
|
120
|
+
result.earliest_ts = event.ts
|
|
121
|
+
if result.latest_ts is None or event.ts > result.latest_ts:
|
|
122
|
+
result.latest_ts = event.ts
|
|
123
|
+
_accumulate(result.artefacts, event.consulted, event.applied, event.ts)
|
|
124
|
+
return result
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _accumulate(
|
|
128
|
+
bucket: dict[tuple[str, str], dict[str, object]],
|
|
129
|
+
consulted: dict[str, list[str]],
|
|
130
|
+
applied: dict[str, list[str]],
|
|
131
|
+
ts: str,
|
|
132
|
+
) -> None:
|
|
133
|
+
for kind, ids in consulted.items():
|
|
134
|
+
for art_id in ids:
|
|
135
|
+
entry = bucket.setdefault((kind, art_id), {"consulted": 0, "applied": 0, "last_seen_ts": ""})
|
|
136
|
+
entry["consulted"] = int(entry["consulted"]) + 1 # type: ignore[operator]
|
|
137
|
+
if ts > str(entry["last_seen_ts"]):
|
|
138
|
+
entry["last_seen_ts"] = ts
|
|
139
|
+
for kind, ids in applied.items():
|
|
140
|
+
for art_id in ids:
|
|
141
|
+
entry = bucket.setdefault((kind, art_id), {"consulted": 0, "applied": 0, "last_seen_ts": ""})
|
|
142
|
+
entry["applied"] = int(entry["applied"]) + 1 # type: ignore[operator]
|
|
143
|
+
if ts > str(entry["last_seen_ts"]):
|
|
144
|
+
entry["last_seen_ts"] = ts
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def rank_artefacts(stats: Iterable[ArtefactStat]) -> list[ArtefactStat]:
|
|
148
|
+
return sorted(
|
|
149
|
+
stats,
|
|
150
|
+
key=lambda s: (-s.applied, -s.consulted, s.kind, s.artefact_id),
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
__all__ = ["ArtefactStat", "AggregateResult", "aggregate", "rank_artefacts"]
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Boundary detection + concurrent-safe recording (Phase 2).
|
|
2
|
+
|
|
3
|
+
Two responsibilities, one module:
|
|
4
|
+
|
|
5
|
+
1. ``BoundarySession`` — in-process coalescing. Multiple ``add_*`` calls
|
|
6
|
+
within one task / phase-step / tool-call boundary merge into a
|
|
7
|
+
single emitted event (set-union on ``consulted`` / ``applied``).
|
|
8
|
+
Idempotent: calling ``flush()`` twice without new additions is a
|
|
9
|
+
no-op; calling ``add_consulted("skills", ["x"])`` twice records
|
|
10
|
+
``"x"`` once.
|
|
11
|
+
|
|
12
|
+
2. ``record_event`` — cross-process durability. Uses ``fcntl.flock``
|
|
13
|
+
(POSIX) so concurrent writers from separate ``./agent-config
|
|
14
|
+
telemetry:record`` invocations cannot interleave inside one JSONL
|
|
15
|
+
line. On non-POSIX (no ``fcntl``) we fall back to a best-effort
|
|
16
|
+
append; the package only ships on POSIX-compatible CI today.
|
|
17
|
+
|
|
18
|
+
The CLI in ``cli.py`` is the only caller that should touch the log
|
|
19
|
+
path directly. Agent-side flows wire through ``BoundarySession``.
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import os
|
|
24
|
+
from contextlib import contextmanager
|
|
25
|
+
from dataclasses import dataclass, field
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Iterable, Iterator
|
|
28
|
+
|
|
29
|
+
try: # POSIX advisory file locking
|
|
30
|
+
import fcntl # type: ignore[import-not-found]
|
|
31
|
+
_HAS_FCNTL = True
|
|
32
|
+
except ImportError: # pragma: no cover — Windows / sandbox
|
|
33
|
+
_HAS_FCNTL = False
|
|
34
|
+
|
|
35
|
+
from .engagement import (
|
|
36
|
+
ALLOWED_BOUNDARY_KINDS,
|
|
37
|
+
ALLOWED_KINDS,
|
|
38
|
+
EngagementEvent,
|
|
39
|
+
EngagementSchemaError,
|
|
40
|
+
now_utc_iso,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class BoundarySession:
|
|
46
|
+
"""Collect artefact engagements for one boundary, flush once.
|
|
47
|
+
|
|
48
|
+
Use as a context manager — ``__exit__`` flushes on clean exit and
|
|
49
|
+
suppresses on exception (so failed tasks don't pollute the log).
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
task_id: str
|
|
53
|
+
boundary_kind: str
|
|
54
|
+
log_path: Path
|
|
55
|
+
consulted: dict[str, set[str]] = field(default_factory=dict)
|
|
56
|
+
applied: dict[str, set[str]] = field(default_factory=dict)
|
|
57
|
+
_flushed: bool = False
|
|
58
|
+
_has_data: bool = False
|
|
59
|
+
|
|
60
|
+
def __post_init__(self) -> None:
|
|
61
|
+
if self.boundary_kind not in ALLOWED_BOUNDARY_KINDS:
|
|
62
|
+
raise EngagementSchemaError(
|
|
63
|
+
f"boundary_kind must be one of {ALLOWED_BOUNDARY_KINDS!r}"
|
|
64
|
+
)
|
|
65
|
+
if not isinstance(self.task_id, str) or not self.task_id:
|
|
66
|
+
raise EngagementSchemaError("task_id must be a non-empty string")
|
|
67
|
+
|
|
68
|
+
def add_consulted(self, kind: str, ids: Iterable[str]) -> None:
|
|
69
|
+
self._merge(self.consulted, kind, ids)
|
|
70
|
+
|
|
71
|
+
def add_applied(self, kind: str, ids: Iterable[str]) -> None:
|
|
72
|
+
self._merge(self.applied, kind, ids)
|
|
73
|
+
|
|
74
|
+
def _merge(self, bucket: dict[str, set[str]], kind: str, ids: Iterable[str]) -> None:
|
|
75
|
+
if kind not in ALLOWED_KINDS:
|
|
76
|
+
raise EngagementSchemaError(
|
|
77
|
+
f"{kind!r} is not an allowed artefact kind "
|
|
78
|
+
f"(allowed: {ALLOWED_KINDS!r})"
|
|
79
|
+
)
|
|
80
|
+
target = bucket.setdefault(kind, set())
|
|
81
|
+
for art_id in ids:
|
|
82
|
+
if not isinstance(art_id, str) or not art_id:
|
|
83
|
+
raise EngagementSchemaError(
|
|
84
|
+
f"{kind} ids must be non-empty strings"
|
|
85
|
+
)
|
|
86
|
+
target.add(art_id)
|
|
87
|
+
self._has_data = True
|
|
88
|
+
|
|
89
|
+
def to_event(self) -> EngagementEvent:
|
|
90
|
+
return EngagementEvent(
|
|
91
|
+
ts=now_utc_iso(),
|
|
92
|
+
task_id=self.task_id,
|
|
93
|
+
boundary_kind=self.boundary_kind,
|
|
94
|
+
consulted={k: sorted(v) for k, v in self.consulted.items() if v},
|
|
95
|
+
applied={k: sorted(v) for k, v in self.applied.items() if v},
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def flush(self) -> bool:
|
|
99
|
+
"""Write one merged event to the log. Returns True if written.
|
|
100
|
+
|
|
101
|
+
No-op when already flushed or no data was added — keeps the
|
|
102
|
+
boundary idempotent.
|
|
103
|
+
"""
|
|
104
|
+
if self._flushed or not self._has_data:
|
|
105
|
+
return False
|
|
106
|
+
record_event(self.log_path, self.to_event())
|
|
107
|
+
self._flushed = True
|
|
108
|
+
return True
|
|
109
|
+
|
|
110
|
+
def __enter__(self) -> "BoundarySession":
|
|
111
|
+
return self
|
|
112
|
+
|
|
113
|
+
def __exit__(self, exc_type: object, exc: object, tb: object) -> None:
|
|
114
|
+
if exc_type is None:
|
|
115
|
+
self.flush()
|
|
116
|
+
# On exception: do nothing — failed boundary, no record.
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def record_event(log_path: Path, event: EngagementEvent) -> None:
|
|
120
|
+
"""Append one event under an exclusive file lock.
|
|
121
|
+
|
|
122
|
+
The lock guarantees that two concurrent writers append two
|
|
123
|
+
complete, well-formed lines instead of one interleaved line. We
|
|
124
|
+
open with ``"a"`` so each write atomically extends EOF on POSIX
|
|
125
|
+
once the lock is held.
|
|
126
|
+
"""
|
|
127
|
+
event.validate()
|
|
128
|
+
payload = event.to_jsonl().encode("utf-8")
|
|
129
|
+
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
fd = os.open(log_path, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o644)
|
|
131
|
+
try:
|
|
132
|
+
if _HAS_FCNTL:
|
|
133
|
+
fcntl.flock(fd, fcntl.LOCK_EX)
|
|
134
|
+
try:
|
|
135
|
+
written = 0
|
|
136
|
+
while written < len(payload):
|
|
137
|
+
written += os.write(fd, payload[written:])
|
|
138
|
+
os.fsync(fd)
|
|
139
|
+
finally:
|
|
140
|
+
if _HAS_FCNTL:
|
|
141
|
+
fcntl.flock(fd, fcntl.LOCK_UN)
|
|
142
|
+
finally:
|
|
143
|
+
os.close(fd)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@contextmanager
|
|
147
|
+
def open_boundary(
|
|
148
|
+
task_id: str,
|
|
149
|
+
boundary_kind: str,
|
|
150
|
+
log_path: Path,
|
|
151
|
+
) -> Iterator[BoundarySession]:
|
|
152
|
+
"""Convenience context manager around ``BoundarySession``.
|
|
153
|
+
|
|
154
|
+
>>> with open_boundary("ticket-1", "task", Path(".agent-engagement.jsonl")) as s:
|
|
155
|
+
... s.add_consulted("skills", ["php-coder"])
|
|
156
|
+
... s.add_applied("skills", ["php-coder"])
|
|
157
|
+
"""
|
|
158
|
+
session = BoundarySession(
|
|
159
|
+
task_id=task_id,
|
|
160
|
+
boundary_kind=boundary_kind,
|
|
161
|
+
log_path=log_path,
|
|
162
|
+
)
|
|
163
|
+
with session:
|
|
164
|
+
yield session
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
__all__ = [
|
|
168
|
+
"BoundarySession",
|
|
169
|
+
"open_boundary",
|
|
170
|
+
"record_event",
|
|
171
|
+
]
|