@event4u/agent-config 1.13.0 → 1.15.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 +4 -1
- 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 +7 -3
- 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 +6 -1
- package/.agent-src/commands/chat-history-resume.md +7 -2
- package/.agent-src/commands/chat-history.md +7 -2
- package/.agent-src/commands/check-current-md.md +137 -0
- package/.agent-src/commands/commit-in-chunks.md +118 -0
- package/.agent-src/commands/commit.md +4 -0
- package/.agent-src/commands/compress.md +37 -2
- 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 +5 -2
- 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 +33 -0
- package/.agent-src/commands/optimize-agents.md +4 -0
- package/.agent-src/commands/optimize-augmentignore.md +12 -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 +12 -7
- package/.agent-src/commands/review-changes.md +39 -8
- package/.agent-src/commands/review-routing.md +4 -0
- package/.agent-src/commands/roadmap-create.md +18 -0
- package/.agent-src/commands/roadmap-execute.md +14 -1
- package/.agent-src/commands/rule-compliance-audit.md +4 -0
- package/.agent-src/commands/set-cost-profile.md +11 -0
- package/.agent-src/commands/sync-agent-settings.md +12 -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 +6 -3
- 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 +64 -37
- package/.agent-src/rules/autonomous-execution.md +158 -0
- package/.agent-src/rules/chat-history-cadence.md +109 -0
- package/.agent-src/rules/chat-history-ownership.md +123 -0
- package/.agent-src/rules/chat-history-visibility.md +96 -0
- package/.agent-src/rules/cli-output-handling.md +27 -4
- package/.agent-src/rules/command-suggestion.md +134 -0
- package/.agent-src/rules/commit-policy.md +109 -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 +85 -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 +159 -27
- package/.agent-src/rules/role-mode-adherence.md +1 -1
- package/.agent-src/rules/scope-control.md +42 -1
- package/.agent-src/rules/size-enforcement.md +2 -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 +107 -51
- package/.agent-src/scripts/update_roadmap_progress.py +73 -9
- package/.agent-src/skills/blade-ui/SKILL.md +47 -3
- package/.agent-src/skills/command-routing/SKILL.md +32 -0
- package/.agent-src/skills/command-writing/SKILL.md +52 -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 +202 -0
- package/.agent-src/skills/fe-design/SKILL.md +78 -61
- package/.agent-src/skills/file-editor/SKILL.md +9 -0
- 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 +49 -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 +32 -28
- package/.agent-src/skills/roadmap-management/SKILL.md +24 -11
- package/.agent-src/skills/rule-writing/SKILL.md +23 -1
- package/.agent-src/skills/skill-writing/SKILL.md +3 -5
- package/.agent-src/skills/upstream-contribute/SKILL.md +3 -3
- 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 +11 -4
- 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/memory_lookup.py +1 -1
- 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 +195 -0
- package/.agent-src/templates/scripts/work_engine/cli_args.py +116 -0
- package/.agent-src/templates/scripts/{implement_ticket → work_engine}/delivery_state.py +10 -3
- 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 +3 -3
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/memory.py +2 -2
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/plan.py +2 -2
- 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 +37 -5
- 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/emitters.py +43 -0
- package/.agent-src/templates/scripts/work_engine/errors.py +19 -0
- package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +76 -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/input_builders.py +163 -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 +231 -0
- package/.agent-src/templates/scripts/{implement_ticket → work_engine}/persona_policy.py +1 -1
- 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/.agent-src/templates/scripts/work_engine/state_io.py +202 -0
- package/.claude-plugin/marketplace.json +105 -2
- package/AGENTS.md +38 -8
- package/CHANGELOG.md +609 -0
- package/README.md +136 -14
- package/config/agent-settings.template.yml +45 -0
- package/config/gitignore-block.txt +4 -0
- package/docs/MIGRATION.md +122 -0
- package/docs/architecture.md +111 -35
- package/docs/contracts/STABILITY.md +95 -0
- package/docs/contracts/adr-chat-history-split.md +132 -0
- package/docs/contracts/adr-command-suggestion.md +146 -0
- package/docs/contracts/adr-implement-ticket-runtime.md +122 -0
- package/docs/contracts/adr-product-ui-track.md +384 -0
- package/docs/contracts/adr-prompt-driven-execution.md +187 -0
- package/docs/contracts/agent-memory-contract.md +149 -0
- package/docs/contracts/artifact-engagement-flow.md +262 -0
- package/docs/contracts/command-clusters.md +126 -0
- package/docs/contracts/command-suggestion-flow.md +148 -0
- package/docs/contracts/implement-ticket-flow.md +628 -0
- package/docs/contracts/linear-ai-rules-inclusion.md +143 -0
- package/docs/contracts/linear-ai-three-layers.md +131 -0
- package/docs/contracts/rule-interactions.md +107 -0
- package/docs/contracts/rule-interactions.yml +142 -0
- package/docs/contracts/ui-stack-extension.md +236 -0
- package/docs/contracts/ui-track-flow.md +338 -0
- package/docs/development.md +1 -1
- package/docs/getting-started.md +3 -3
- package/docs/installation.md +124 -2
- package/docs/migrations/commands-1.15.0.md +112 -0
- package/docs/showcase.md +204 -0
- package/docs/ui-track-mental-model.md +121 -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 +38 -0
- package/scripts/check_public_links.py +185 -0
- package/scripts/check_references.py +1 -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/lint_no_new_atomic_commands.py +179 -0
- package/scripts/lint_rule_interactions.py +149 -0
- package/scripts/memory_lookup.py +1 -1
- package/scripts/migrate_command_suggestions.py +151 -0
- package/scripts/release.py +297 -64
- package/scripts/schemas/command.schema.json +41 -0
- package/scripts/skill_linter.py +81 -0
- package/scripts/sync_agent_settings.py +42 -12
- package/scripts/update_counts.py +10 -0
- package/templates/consumer-settings/augment-cli-hooks.json +54 -0
- package/templates/consumer-settings/claude-settings.json +55 -1
- package/.agent-src/rules/chat-history.md +0 -171
- 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
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""``HookRunner`` — single emit point for hook callbacks.
|
|
2
|
+
|
|
3
|
+
Implements the three-tier error contract documented in
|
|
4
|
+
``exceptions.py``:
|
|
5
|
+
|
|
6
|
+
- ``HookError`` from a callback → caught, ``warnings.warn`` is emitted,
|
|
7
|
+
the runner continues with the next callback for the same event.
|
|
8
|
+
Returns ``None`` once the event is fully drained.
|
|
9
|
+
- ``HookHalt`` from a callback → caught, **returned** to the caller
|
|
10
|
+
with no further callbacks invoked for this event. The caller
|
|
11
|
+
decides how to surface the halt (engine halt, CLI exit 2). Never
|
|
12
|
+
re-raised through the dispatch loop.
|
|
13
|
+
- any other ``Exception`` → propagates unchanged. Treated as a hook
|
|
14
|
+
bug; dispatch unwinds.
|
|
15
|
+
|
|
16
|
+
The runner is intentionally tiny. Behavior changes belong here so
|
|
17
|
+
``dispatcher.py`` and ``cli.py`` stay free of hook bookkeeping.
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import warnings
|
|
22
|
+
|
|
23
|
+
from .context import HookContext
|
|
24
|
+
from .events import HookEvent
|
|
25
|
+
from .exceptions import HookError, HookHalt
|
|
26
|
+
from .registry import HookRegistry
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class HookRunner:
|
|
30
|
+
"""Emit hook events through a :class:`HookRegistry`.
|
|
31
|
+
|
|
32
|
+
Construct once per CLI invocation, share between the CLI and the
|
|
33
|
+
dispatcher. ``emit`` is the only public method on the hot path.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, registry: HookRegistry | None = None) -> None:
|
|
37
|
+
self._registry = registry if registry is not None else HookRegistry()
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def registry(self) -> HookRegistry:
|
|
41
|
+
"""Return the underlying registry.
|
|
42
|
+
|
|
43
|
+
Exposed so callers can register additional hooks after
|
|
44
|
+
construction (e.g. in tests). Not used on the hot path.
|
|
45
|
+
"""
|
|
46
|
+
return self._registry
|
|
47
|
+
|
|
48
|
+
def emit(self, event: HookEvent, ctx: HookContext) -> HookHalt | None:
|
|
49
|
+
"""Fire all callbacks registered for ``event``.
|
|
50
|
+
|
|
51
|
+
Returns ``None`` when every callback completed (with or without
|
|
52
|
+
a swallowed :class:`HookError`). Returns the first
|
|
53
|
+
:class:`HookHalt` raised, after which no further callbacks are
|
|
54
|
+
invoked for this event. Any other exception propagates.
|
|
55
|
+
"""
|
|
56
|
+
callbacks = self._registry.for_event(event)
|
|
57
|
+
if not callbacks:
|
|
58
|
+
return None
|
|
59
|
+
for callback in callbacks:
|
|
60
|
+
try:
|
|
61
|
+
callback(ctx)
|
|
62
|
+
except HookHalt as halt:
|
|
63
|
+
return halt
|
|
64
|
+
except HookError as err:
|
|
65
|
+
warnings.warn(
|
|
66
|
+
f"hook {event.value} raised HookError: {err}",
|
|
67
|
+
stacklevel=2,
|
|
68
|
+
)
|
|
69
|
+
continue
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
__all__ = ["HookRunner"]
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Read ``hooks.*`` from ``.agent-settings.yml`` into :class:`HookSettings`.
|
|
2
|
+
|
|
3
|
+
Mirror of the chat-history settings pattern (``scripts/command_suggester/
|
|
4
|
+
settings.py``):
|
|
5
|
+
|
|
6
|
+
* Lazy PyYAML import — the engine works without yaml installed when no
|
|
7
|
+
settings file is present (test fixtures, cloud bundles).
|
|
8
|
+
* Default-permissive: a missing file or missing ``hooks:`` block returns
|
|
9
|
+
:class:`HookSettings` with ``enabled=False`` — every hook off, every
|
|
10
|
+
golden replay safe by construction.
|
|
11
|
+
* Malformed YAML / unreadable file → defaults; the engine degrades
|
|
12
|
+
silently rather than crashing the CLI.
|
|
13
|
+
* Chat-history hooks gate on **two** switches: ``hooks.chat_history.
|
|
14
|
+
enabled`` AND the global ``chat_history.enabled``. Either off → no
|
|
15
|
+
chat-history hook registers.
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
DEFAULT_SETTINGS_FILE = ".agent-settings.yml"
|
|
24
|
+
DEFAULT_CHAT_HISTORY_SCRIPT = "scripts/chat_history.py"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class HookSettings:
|
|
29
|
+
"""Resolved view of the ``hooks:`` block.
|
|
30
|
+
|
|
31
|
+
``enabled`` is the master switch. When ``False`` the registry stays
|
|
32
|
+
empty regardless of the per-hook fields; this is the default when no
|
|
33
|
+
settings file exists or no ``hooks`` block is declared, and it is
|
|
34
|
+
what keeps golden-replay tests byte-stable.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
enabled: bool = False
|
|
38
|
+
trace: bool = False
|
|
39
|
+
halt_surface_audit: bool = False
|
|
40
|
+
state_shape_validation: bool = False
|
|
41
|
+
directive_set_guard: bool = False
|
|
42
|
+
chat_history_enabled: bool = False
|
|
43
|
+
chat_history_script: str = DEFAULT_CHAT_HISTORY_SCRIPT
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
_DEFAULT = HookSettings()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def load_hook_settings(
|
|
50
|
+
settings_path: Path | str | None = None,
|
|
51
|
+
) -> HookSettings:
|
|
52
|
+
"""Return :class:`HookSettings` hydrated from ``.agent-settings.yml``.
|
|
53
|
+
|
|
54
|
+
``settings_path`` defaults to ``./.agent-settings.yml`` relative to
|
|
55
|
+
the current working directory — same convention as chat-history.
|
|
56
|
+
"""
|
|
57
|
+
path = Path(settings_path) if settings_path else Path(DEFAULT_SETTINGS_FILE)
|
|
58
|
+
raw = _read_yaml(path)
|
|
59
|
+
if raw is None:
|
|
60
|
+
return _DEFAULT
|
|
61
|
+
return _settings_from_raw(raw)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _read_yaml(path: Path) -> dict[str, Any] | None:
|
|
65
|
+
if not path.is_file():
|
|
66
|
+
return None
|
|
67
|
+
try:
|
|
68
|
+
import yaml # type: ignore[import-untyped]
|
|
69
|
+
except ImportError:
|
|
70
|
+
return None
|
|
71
|
+
try:
|
|
72
|
+
with path.open(encoding="utf-8") as fh:
|
|
73
|
+
data = yaml.safe_load(fh) or {}
|
|
74
|
+
except (OSError, yaml.YAMLError):
|
|
75
|
+
return None
|
|
76
|
+
if not isinstance(data, dict):
|
|
77
|
+
return None
|
|
78
|
+
return data
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _settings_from_raw(data: dict[str, Any]) -> HookSettings:
|
|
82
|
+
hooks = data.get("hooks")
|
|
83
|
+
if not isinstance(hooks, dict):
|
|
84
|
+
return _DEFAULT
|
|
85
|
+
enabled = _coerce_bool(hooks.get("enabled"), False)
|
|
86
|
+
if not enabled:
|
|
87
|
+
return HookSettings(enabled=False)
|
|
88
|
+
|
|
89
|
+
chat_section = hooks.get("chat_history")
|
|
90
|
+
if isinstance(chat_section, dict):
|
|
91
|
+
chat_block_enabled = _coerce_bool(chat_section.get("enabled"), True)
|
|
92
|
+
chat_script = str(
|
|
93
|
+
chat_section.get("script") or DEFAULT_CHAT_HISTORY_SCRIPT
|
|
94
|
+
)
|
|
95
|
+
else:
|
|
96
|
+
chat_block_enabled = True
|
|
97
|
+
chat_script = DEFAULT_CHAT_HISTORY_SCRIPT
|
|
98
|
+
|
|
99
|
+
global_chat = data.get("chat_history")
|
|
100
|
+
global_chat_on = (
|
|
101
|
+
isinstance(global_chat, dict)
|
|
102
|
+
and _coerce_bool(global_chat.get("enabled"), False)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return HookSettings(
|
|
106
|
+
enabled=True,
|
|
107
|
+
trace=_coerce_bool(hooks.get("trace"), False),
|
|
108
|
+
halt_surface_audit=_coerce_bool(
|
|
109
|
+
hooks.get("halt_surface_audit"), True
|
|
110
|
+
),
|
|
111
|
+
state_shape_validation=_coerce_bool(
|
|
112
|
+
hooks.get("state_shape_validation"), True
|
|
113
|
+
),
|
|
114
|
+
directive_set_guard=_coerce_bool(
|
|
115
|
+
hooks.get("directive_set_guard"), True
|
|
116
|
+
),
|
|
117
|
+
chat_history_enabled=chat_block_enabled and global_chat_on,
|
|
118
|
+
chat_history_script=chat_script,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _coerce_bool(value: Any, default: bool) -> bool:
|
|
123
|
+
if isinstance(value, bool):
|
|
124
|
+
return value
|
|
125
|
+
if value is None:
|
|
126
|
+
return default
|
|
127
|
+
if isinstance(value, str):
|
|
128
|
+
s = value.strip().lower()
|
|
129
|
+
if s in ("true", "yes", "on", "1"):
|
|
130
|
+
return True
|
|
131
|
+
if s in ("false", "no", "off", "0"):
|
|
132
|
+
return False
|
|
133
|
+
return default
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
__all__ = [
|
|
137
|
+
"DEFAULT_CHAT_HISTORY_SCRIPT",
|
|
138
|
+
"DEFAULT_SETTINGS_FILE",
|
|
139
|
+
"HookSettings",
|
|
140
|
+
"load_hook_settings",
|
|
141
|
+
]
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""File-based input builders and the load-or-build dispatch helper.
|
|
2
|
+
|
|
3
|
+
Extracted from ``cli.py`` in P2.3 of
|
|
4
|
+
``road-to-post-pr29-optimize.md``. Owns the CLI's "first run" path:
|
|
5
|
+
when no state file exists, build a fresh :class:`WorkState` from
|
|
6
|
+
``--ticket-file``, ``--prompt-file``, ``--diff-file`` or
|
|
7
|
+
``--file-file``. Every builder is byte-identical in behaviour to the
|
|
8
|
+
pre-split version — the resolvers it calls and the persona / routing
|
|
9
|
+
post-processing did not move.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from .cli_args import _FMT_V0, _FMT_V1
|
|
17
|
+
from .errors import _CLIError
|
|
18
|
+
from .intent import populate_routing
|
|
19
|
+
from .resolvers.diff import DiffResolverError, build_envelope as _build_diff_envelope
|
|
20
|
+
from .resolvers.file import FileResolverError, build_envelope as _build_file_envelope
|
|
21
|
+
from .resolvers.prompt import PromptResolverError, build_envelope as _build_prompt_envelope
|
|
22
|
+
from .state import Input, WorkState
|
|
23
|
+
from .state_io import _load, _maybe_raise_legacy_hint, _read_json
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _load_or_build(
|
|
27
|
+
state_file: Path,
|
|
28
|
+
args: argparse.Namespace,
|
|
29
|
+
) -> tuple[WorkState, str]:
|
|
30
|
+
"""Return the WorkState to dispatch against plus its wire format.
|
|
31
|
+
|
|
32
|
+
Either loaded from ``state_file`` (format-preserving) or freshly
|
|
33
|
+
built from ``--ticket-file`` (R1), ``--prompt-file`` (R2),
|
|
34
|
+
``--diff-file`` (R3) or ``--file-file`` (R3). Fresh ticket files
|
|
35
|
+
default to v0 wire format so that newly captured Goldens stay
|
|
36
|
+
byte-equal with the pre-Phase-4 baseline; the prompt / diff / file
|
|
37
|
+
paths emit v1 directly (v0 has no envelope concept for these
|
|
38
|
+
kinds). v1 round-trips for state files already on disk in v1 shape.
|
|
39
|
+
"""
|
|
40
|
+
if state_file.exists():
|
|
41
|
+
return _load(state_file)
|
|
42
|
+
_maybe_raise_legacy_hint(state_file)
|
|
43
|
+
inputs = [
|
|
44
|
+
("--ticket-file", args.ticket_file),
|
|
45
|
+
("--prompt-file", args.prompt_file),
|
|
46
|
+
("--diff-file", args.diff_file),
|
|
47
|
+
("--file-file", args.file_file),
|
|
48
|
+
]
|
|
49
|
+
supplied = [name for name, value in inputs if value is not None]
|
|
50
|
+
if len(supplied) > 1:
|
|
51
|
+
raise _CLIError(
|
|
52
|
+
f"{', '.join(supplied)} are mutually exclusive; pass exactly "
|
|
53
|
+
"one when building an initial state.",
|
|
54
|
+
)
|
|
55
|
+
if not supplied:
|
|
56
|
+
raise _CLIError(
|
|
57
|
+
f"No state file at {state_file} and no --ticket-file, "
|
|
58
|
+
"--prompt-file, --diff-file, or --file-file given; cannot "
|
|
59
|
+
"build an initial state.",
|
|
60
|
+
)
|
|
61
|
+
if args.prompt_file is not None:
|
|
62
|
+
return _build_from_prompt_file(args), _FMT_V1
|
|
63
|
+
if args.diff_file is not None:
|
|
64
|
+
return _build_from_diff_file(args), _FMT_V1
|
|
65
|
+
if args.file_file is not None:
|
|
66
|
+
return _build_from_file_file(args), _FMT_V1
|
|
67
|
+
ticket = _read_json(args.ticket_file)
|
|
68
|
+
if not isinstance(ticket, dict):
|
|
69
|
+
raise _CLIError(
|
|
70
|
+
f"--ticket-file must carry a JSON object; got {type(ticket).__name__}.",
|
|
71
|
+
)
|
|
72
|
+
work = WorkState(input=Input(kind="ticket", data=ticket))
|
|
73
|
+
if args.persona:
|
|
74
|
+
work.persona = args.persona
|
|
75
|
+
populate_routing(work)
|
|
76
|
+
return work, _FMT_V0
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _build_from_prompt_file(args: argparse.Namespace) -> WorkState:
|
|
80
|
+
"""Read ``--prompt-file`` as raw text and wrap it in a prompt envelope.
|
|
81
|
+
|
|
82
|
+
The file is read verbatim (UTF-8) and handed to the prompt resolver,
|
|
83
|
+
which validates non-emptiness and returns the canonical
|
|
84
|
+
``Input(kind="prompt", data={raw, reconstructed_ac, assumptions})``
|
|
85
|
+
envelope. Persona is honoured the same way as the ticket path.
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
raw = args.prompt_file.read_text(encoding="utf-8")
|
|
89
|
+
except OSError as exc:
|
|
90
|
+
raise _CLIError(f"Cannot read {args.prompt_file}: {exc}") from exc
|
|
91
|
+
try:
|
|
92
|
+
envelope = _build_prompt_envelope(raw)
|
|
93
|
+
except PromptResolverError as exc:
|
|
94
|
+
raise _CLIError(f"--prompt-file is not a valid prompt: {exc}") from exc
|
|
95
|
+
work = WorkState(input=envelope)
|
|
96
|
+
if args.persona:
|
|
97
|
+
work.persona = args.persona
|
|
98
|
+
populate_routing(work)
|
|
99
|
+
return work
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _build_from_diff_file(args: argparse.Namespace) -> WorkState:
|
|
103
|
+
"""Read ``--diff-file`` as raw text and wrap it in a diff envelope.
|
|
104
|
+
|
|
105
|
+
The file is read verbatim (UTF-8) and handed to the diff resolver,
|
|
106
|
+
which validates the unified-diff header heuristic and returns the
|
|
107
|
+
canonical
|
|
108
|
+
``Input(kind="diff", data={raw, reconstructed_ac, assumptions})``
|
|
109
|
+
envelope. ``populate_routing`` then routes the envelope to the
|
|
110
|
+
UI-improve directive set without running the prose classifier — see
|
|
111
|
+
:mod:`work_engine.intent.classify` for the routing contract.
|
|
112
|
+
"""
|
|
113
|
+
try:
|
|
114
|
+
raw = args.diff_file.read_text(encoding="utf-8")
|
|
115
|
+
except OSError as exc:
|
|
116
|
+
raise _CLIError(f"Cannot read {args.diff_file}: {exc}") from exc
|
|
117
|
+
try:
|
|
118
|
+
envelope = _build_diff_envelope(raw)
|
|
119
|
+
except DiffResolverError as exc:
|
|
120
|
+
raise _CLIError(f"--diff-file is not a valid diff: {exc}") from exc
|
|
121
|
+
work = WorkState(input=envelope)
|
|
122
|
+
if args.persona:
|
|
123
|
+
work.persona = args.persona
|
|
124
|
+
populate_routing(work)
|
|
125
|
+
return work
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _build_from_file_file(args: argparse.Namespace) -> WorkState:
|
|
129
|
+
"""Read ``--file-file`` as a single-line path and wrap it in a file envelope.
|
|
130
|
+
|
|
131
|
+
The file is read verbatim (UTF-8); the first non-empty line is taken
|
|
132
|
+
as the path reference and handed to the file resolver, which
|
|
133
|
+
validates path shape (non-empty, NUL-free, not a URL) and returns
|
|
134
|
+
the canonical
|
|
135
|
+
``Input(kind="file", data={path, reconstructed_ac, assumptions})``
|
|
136
|
+
envelope. Trailing whitespace and additional lines are ignored —
|
|
137
|
+
the resolver treats the file's content as the path itself, not as
|
|
138
|
+
structured payload.
|
|
139
|
+
"""
|
|
140
|
+
try:
|
|
141
|
+
raw = args.file_file.read_text(encoding="utf-8")
|
|
142
|
+
except OSError as exc:
|
|
143
|
+
raise _CLIError(f"Cannot read {args.file_file}: {exc}") from exc
|
|
144
|
+
path = raw.strip().splitlines()[0] if raw.strip() else ""
|
|
145
|
+
try:
|
|
146
|
+
envelope = _build_file_envelope(path)
|
|
147
|
+
except FileResolverError as exc:
|
|
148
|
+
raise _CLIError(
|
|
149
|
+
f"--file-file does not carry a valid path: {exc}",
|
|
150
|
+
) from exc
|
|
151
|
+
work = WorkState(input=envelope)
|
|
152
|
+
if args.persona:
|
|
153
|
+
work.persona = args.persona
|
|
154
|
+
populate_routing(work)
|
|
155
|
+
return work
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
__all__ = [
|
|
159
|
+
"_build_from_diff_file",
|
|
160
|
+
"_build_from_file_file",
|
|
161
|
+
"_build_from_prompt_file",
|
|
162
|
+
"_load_or_build",
|
|
163
|
+
]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Intent classification for the universal engine (R3 Phase 1 Step 2).
|
|
2
|
+
|
|
3
|
+
The :mod:`work_engine.intent.classify` module turns a raw user prompt
|
|
4
|
+
(or a ticket's title + body) into one of the five labels the dispatcher
|
|
5
|
+
routes against:
|
|
6
|
+
|
|
7
|
+
- ``ui-build`` — new screen, page, or component.
|
|
8
|
+
- ``ui-improve`` — change to an existing screen / component.
|
|
9
|
+
- ``ui-trivial`` — single-file, single-concern micro-edit (color, copy,
|
|
10
|
+
one class, one prop). Hard preconditions are enforced again at apply
|
|
11
|
+
time; the classifier only labels the *intent*, not the safety floor.
|
|
12
|
+
- ``mixed`` — both UI and backend signals; routes to the mixed track.
|
|
13
|
+
- ``backend-coding`` — default; no UI signal.
|
|
14
|
+
|
|
15
|
+
The classifier is intentionally heuristic-only — it consumes nothing
|
|
16
|
+
beyond the prompt text and optional ticket title. Confidence-band
|
|
17
|
+
gating, AC reconstruction, and assumption surfacing all stay in
|
|
18
|
+
``directives/backend/refine.py`` (R2). This module only owns the
|
|
19
|
+
*label*; the dispatcher owns the routing.
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from . import classify
|
|
24
|
+
from .classify import (
|
|
25
|
+
INTENT_BACKEND,
|
|
26
|
+
INTENT_MIXED,
|
|
27
|
+
INTENT_UI_BUILD,
|
|
28
|
+
INTENT_UI_IMPROVE,
|
|
29
|
+
INTENT_UI_TRIVIAL,
|
|
30
|
+
KNOWN_INTENTS,
|
|
31
|
+
classify_intent,
|
|
32
|
+
directive_set_for,
|
|
33
|
+
populate_routing,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"INTENT_BACKEND",
|
|
38
|
+
"INTENT_MIXED",
|
|
39
|
+
"INTENT_UI_BUILD",
|
|
40
|
+
"INTENT_UI_IMPROVE",
|
|
41
|
+
"INTENT_UI_TRIVIAL",
|
|
42
|
+
"KNOWN_INTENTS",
|
|
43
|
+
"classify",
|
|
44
|
+
"classify_intent",
|
|
45
|
+
"directive_set_for",
|
|
46
|
+
"populate_routing",
|
|
47
|
+
]
|