@event4u/agent-config 1.12.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 +81 -3
- package/.agent-src/scripts/update_roadmap_progress.py +48 -6
- 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/github-workflows/roadmap-progress-check.yml +63 -0
- package/.agent-src/templates/hooks/pre-commit-roadmap-progress +60 -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/memory_lookup.py +382 -21
- package/.agent-src/templates/scripts/memory_status.py +110 -9
- 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 +558 -0
- package/README.md +146 -4
- package/composer.json +3 -0
- 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 +3 -2
- package/docs/installation.md +86 -0
- package/docs/showcase.md +204 -0
- package/package.json +9 -1
- package/scripts/agent-config +274 -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/memory_lookup.py +143 -7
- package/scripts/memory_status.py +76 -14
- package/scripts/migrate_command_suggestions.py +151 -0
- package/scripts/postinstall.sh +16 -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
package/scripts/skill_linter.py
CHANGED
|
@@ -772,6 +772,70 @@ def lint_rule(path: Path, text: str) -> LintResult:
|
|
|
772
772
|
)
|
|
773
773
|
|
|
774
774
|
|
|
775
|
+
def _lint_command_suggestion_block(text: str) -> List[Issue]:
|
|
776
|
+
"""Validate the suggestion frontmatter block (road-to-context-aware-command-suggestion).
|
|
777
|
+
|
|
778
|
+
Schema-shape is enforced upstream by validate_frontmatter; this function adds the
|
|
779
|
+
*conditional* content rules that JSON Schema (Draft-07 subset used here) cannot
|
|
780
|
+
express: trigger fields must be non-empty when eligible, rationale must be
|
|
781
|
+
non-empty when ineligible.
|
|
782
|
+
"""
|
|
783
|
+
issues: List[Issue] = []
|
|
784
|
+
data, _offset = parse_frontmatter_for_schema(text)
|
|
785
|
+
if data is None:
|
|
786
|
+
return issues
|
|
787
|
+
suggestion = data.get("suggestion")
|
|
788
|
+
if suggestion is None:
|
|
789
|
+
issues.append(Issue(
|
|
790
|
+
"warning", "missing_suggestion_block",
|
|
791
|
+
"Command frontmatter is missing the 'suggestion' block — required by "
|
|
792
|
+
"road-to-context-aware-command-suggestion Phase 2.",
|
|
793
|
+
))
|
|
794
|
+
return issues
|
|
795
|
+
if not isinstance(suggestion, dict):
|
|
796
|
+
issues.append(Issue("error", "invalid_suggestion_block", "'suggestion' must be a mapping"))
|
|
797
|
+
return issues
|
|
798
|
+
eligible = suggestion.get("eligible")
|
|
799
|
+
if eligible is True:
|
|
800
|
+
td = (suggestion.get("trigger_description") or "").strip()
|
|
801
|
+
tc = (suggestion.get("trigger_context") or "").strip()
|
|
802
|
+
if not td:
|
|
803
|
+
issues.append(Issue(
|
|
804
|
+
"error", "missing_trigger_description",
|
|
805
|
+
"suggestion.eligible=true requires a non-empty 'trigger_description'.",
|
|
806
|
+
))
|
|
807
|
+
elif len(td) < 10:
|
|
808
|
+
issues.append(Issue(
|
|
809
|
+
"warning", "trigger_description_too_short",
|
|
810
|
+
"suggestion.trigger_description is suspiciously short (<10 chars); "
|
|
811
|
+
"linter rejects empty or overly generic patterns.",
|
|
812
|
+
))
|
|
813
|
+
if not tc:
|
|
814
|
+
issues.append(Issue(
|
|
815
|
+
"error", "missing_trigger_context",
|
|
816
|
+
"suggestion.eligible=true requires a non-empty 'trigger_context'.",
|
|
817
|
+
))
|
|
818
|
+
elif len(tc) < 10:
|
|
819
|
+
issues.append(Issue(
|
|
820
|
+
"warning", "trigger_context_too_short",
|
|
821
|
+
"suggestion.trigger_context is suspiciously short (<10 chars); "
|
|
822
|
+
"linter rejects empty or overly generic patterns.",
|
|
823
|
+
))
|
|
824
|
+
elif eligible is False:
|
|
825
|
+
rationale = (suggestion.get("rationale") or "").strip()
|
|
826
|
+
if not rationale:
|
|
827
|
+
issues.append(Issue(
|
|
828
|
+
"error", "missing_suggestion_rationale",
|
|
829
|
+
"suggestion.eligible=false requires a non-empty 'rationale'.",
|
|
830
|
+
))
|
|
831
|
+
else:
|
|
832
|
+
issues.append(Issue(
|
|
833
|
+
"error", "invalid_suggestion_eligible",
|
|
834
|
+
"suggestion.eligible must be true or false.",
|
|
835
|
+
))
|
|
836
|
+
return issues
|
|
837
|
+
|
|
838
|
+
|
|
775
839
|
def lint_command(path: Path, text: str) -> LintResult:
|
|
776
840
|
issues: List[Issue] = []
|
|
777
841
|
suggestions: List[str] = []
|
|
@@ -800,6 +864,9 @@ def lint_command(path: Path, text: str) -> LintResult:
|
|
|
800
864
|
if not description:
|
|
801
865
|
issues.append(Issue("warning", "missing_description", "Frontmatter description is missing"))
|
|
802
866
|
|
|
867
|
+
# suggestion block (road-to-context-aware-command-suggestion Phase 2)
|
|
868
|
+
issues.extend(_lint_command_suggestion_block(text))
|
|
869
|
+
|
|
803
870
|
# --- Structure checks ---
|
|
804
871
|
if not H1_PATTERN.search(text):
|
|
805
872
|
issues.append(Issue("error", "missing_h1", "Command is missing an H1 heading (# Title)"))
|
|
@@ -47,29 +47,48 @@ DEFAULT_PROFILE_DIR = Path(__file__).resolve().parent.parent / "config" / "profi
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
def _flatten(data: dict, prefix: str = "") -> dict[str, object]:
|
|
50
|
-
"""Flatten nested dicts to dotted keys —
|
|
50
|
+
"""Flatten nested dicts to dotted keys — recurses to all leaves.
|
|
51
|
+
|
|
52
|
+
Lists, scalars, and ``None`` are leaves. Dicts are walked and their
|
|
53
|
+
keys folded into the dotted path.
|
|
54
|
+
"""
|
|
51
55
|
out: dict[str, object] = {}
|
|
52
56
|
for key, value in data.items():
|
|
53
57
|
path = f"{prefix}{key}"
|
|
54
58
|
if isinstance(value, dict):
|
|
55
|
-
|
|
56
|
-
out[f"{path}.{sub_key}"] = sub_val
|
|
59
|
+
out.update(_flatten(value, prefix=f"{path}."))
|
|
57
60
|
else:
|
|
58
61
|
out[path] = value
|
|
59
62
|
return out
|
|
60
63
|
|
|
61
64
|
|
|
62
|
-
def
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
def _as_yaml_value(value: object) -> str | None:
|
|
66
|
+
"""Format *value* as an inline-YAML literal.
|
|
67
|
+
|
|
68
|
+
Returns ``None`` when the value cannot be safely represented as a
|
|
69
|
+
scalar / flow-style sequence (e.g. unsupported types). Callers
|
|
70
|
+
must skip those keys so the template default sticks instead of
|
|
71
|
+
producing malformed YAML.
|
|
72
|
+
"""
|
|
66
73
|
if isinstance(value, bool):
|
|
67
74
|
return "true" if value else "false"
|
|
68
75
|
if isinstance(value, int):
|
|
69
76
|
return str(value)
|
|
77
|
+
if isinstance(value, float):
|
|
78
|
+
return repr(value)
|
|
70
79
|
if value is None:
|
|
71
|
-
return ""
|
|
72
|
-
|
|
80
|
+
return "~"
|
|
81
|
+
if isinstance(value, list):
|
|
82
|
+
items: list[str] = []
|
|
83
|
+
for item in value:
|
|
84
|
+
rendered = _as_yaml_value(item)
|
|
85
|
+
if rendered is None:
|
|
86
|
+
return None
|
|
87
|
+
items.append(rendered)
|
|
88
|
+
return "[" + ", ".join(items) + "]"
|
|
89
|
+
if isinstance(value, str):
|
|
90
|
+
return _install._yaml_scalar(value)
|
|
91
|
+
return None
|
|
73
92
|
|
|
74
93
|
|
|
75
94
|
def _template_keys(template_body: str) -> set[str]:
|
|
@@ -81,10 +100,18 @@ def _template_keys(template_body: str) -> set[str]:
|
|
|
81
100
|
|
|
82
101
|
|
|
83
102
|
def _apply_user_values(template_body: str, user_flat: dict[str, object]) -> str:
|
|
84
|
-
"""Overlay every known user value on the rendered template body.
|
|
103
|
+
"""Overlay every known user value on the rendered template body.
|
|
104
|
+
|
|
105
|
+
Keys whose value cannot be rendered inline (see :func:`_as_yaml_value`)
|
|
106
|
+
are skipped so the template default survives instead of corrupting
|
|
107
|
+
the file.
|
|
108
|
+
"""
|
|
85
109
|
body = template_body
|
|
86
110
|
for dotted, value in user_flat.items():
|
|
87
|
-
|
|
111
|
+
rendered = _as_yaml_value(value)
|
|
112
|
+
if rendered is None:
|
|
113
|
+
continue
|
|
114
|
+
body = _install._replace_template_value_raw(body, dotted, rendered)
|
|
88
115
|
return body
|
|
89
116
|
|
|
90
117
|
|
|
@@ -100,7 +127,10 @@ def _append_unknown(body: str, user_flat: dict[str, object], known: set[str]) ->
|
|
|
100
127
|
"_user:",
|
|
101
128
|
]
|
|
102
129
|
for key in unknown:
|
|
103
|
-
|
|
130
|
+
rendered = _as_yaml_value(user_flat[key])
|
|
131
|
+
if rendered is None:
|
|
132
|
+
continue
|
|
133
|
+
lines.append(f" {key}: {rendered}")
|
|
104
134
|
suffix = "\n".join(lines) + "\n"
|
|
105
135
|
return body + (suffix if body.endswith("\n") else "\n" + suffix)
|
|
106
136
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"// Instructions": "OPTIONAL: merge this into ~/.augment/settings.json (user-scope, NOT project-scope) to wire chat-history hooks for the Augment CLI.",
|
|
3
|
+
"// Why user-scope": "The Augment CLI reads hooks from ~/.augment/settings.json (per https://docs.augmentcode.com/cli/hooks.md). Project-scoped .augment/settings.json belongs to the IDE plugin, which has no documented hook surface as of 2026-04-30.",
|
|
4
|
+
"// IDE plugin": "Use the /chat-history-checkpoint command at phase boundaries instead — see rules/chat-history.md.",
|
|
5
|
+
"// Wrapper": "The command assumes the project-root ./agent-config wrapper is on PATH or invoked with a project-relative cwd. Adjust to absolute paths if your shell does not chdir into the project.",
|
|
6
|
+
|
|
7
|
+
"hooks": {
|
|
8
|
+
"SessionStart": [
|
|
9
|
+
{
|
|
10
|
+
"hooks": [
|
|
11
|
+
{
|
|
12
|
+
"type": "command",
|
|
13
|
+
"command": "./agent-config chat-history:hook --platform augment",
|
|
14
|
+
"timeout": 5000
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"PostToolUse": [
|
|
20
|
+
{
|
|
21
|
+
"matcher": ".*",
|
|
22
|
+
"hooks": [
|
|
23
|
+
{
|
|
24
|
+
"type": "command",
|
|
25
|
+
"command": "./agent-config chat-history:hook --platform augment",
|
|
26
|
+
"timeout": 3000
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"Stop": [
|
|
32
|
+
{
|
|
33
|
+
"hooks": [
|
|
34
|
+
{
|
|
35
|
+
"type": "command",
|
|
36
|
+
"command": "./agent-config chat-history:hook --platform augment",
|
|
37
|
+
"timeout": 3000
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"SessionEnd": [
|
|
43
|
+
{
|
|
44
|
+
"hooks": [
|
|
45
|
+
{
|
|
46
|
+
"type": "command",
|
|
47
|
+
"command": "./agent-config chat-history:hook --platform augment",
|
|
48
|
+
"timeout": 3000
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -1,9 +1,63 @@
|
|
|
1
1
|
{
|
|
2
|
-
"// Instructions": "Copy this into your project's .claude/settings.json to auto-enable the agent-config plugin.",
|
|
2
|
+
"// Instructions": "Copy this into your project's .claude/settings.json to auto-enable the agent-config plugin and chat-history hooks.",
|
|
3
3
|
"// After setup": "Claude Code will load the plugin when the marketplace is installed.",
|
|
4
4
|
"// Install marketplace": "Run: claude plugin marketplace add event4u-app/agent-config",
|
|
5
|
+
"// Hooks": "Hooks call ./agent-config (the wrapper installed at the project root) which dispatches to scripts/chat_history.py hook-dispatch. They are no-ops when chat_history.enabled is false in .agent-settings.yml.",
|
|
5
6
|
|
|
6
7
|
"enabledPlugins": {
|
|
7
8
|
"agent-conf@event4u": true
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
"hooks": {
|
|
12
|
+
"SessionStart": [
|
|
13
|
+
{
|
|
14
|
+
"hooks": [
|
|
15
|
+
{
|
|
16
|
+
"type": "command",
|
|
17
|
+
"command": "./agent-config chat-history:hook --platform claude"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"UserPromptSubmit": [
|
|
23
|
+
{
|
|
24
|
+
"hooks": [
|
|
25
|
+
{
|
|
26
|
+
"type": "command",
|
|
27
|
+
"command": "./agent-config chat-history:hook --platform claude"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"PostToolUse": [
|
|
33
|
+
{
|
|
34
|
+
"hooks": [
|
|
35
|
+
{
|
|
36
|
+
"type": "command",
|
|
37
|
+
"command": "./agent-config chat-history:hook --platform claude"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"Stop": [
|
|
43
|
+
{
|
|
44
|
+
"hooks": [
|
|
45
|
+
{
|
|
46
|
+
"type": "command",
|
|
47
|
+
"command": "./agent-config chat-history:hook --platform claude"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"SessionEnd": [
|
|
53
|
+
{
|
|
54
|
+
"hooks": [
|
|
55
|
+
{
|
|
56
|
+
"type": "command",
|
|
57
|
+
"command": "./agent-config chat-history:hook --platform claude"
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
]
|
|
8
62
|
}
|
|
9
63
|
}
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
"""Command-line entry point for ``/implement-ticket``.
|
|
2
|
-
|
|
3
|
-
Minimal Option-A transport: the script loads a persisted
|
|
4
|
-
``DeliveryState`` (or builds one from a ticket file), runs the
|
|
5
|
-
dispatcher once, writes the updated state back, and prints either
|
|
6
|
-
the delivery report (on SUCCESS) or the halt surface — directive
|
|
7
|
-
plus numbered questions — on BLOCKED/PARTIAL.
|
|
8
|
-
|
|
9
|
-
The script never edits code, runs tests, or opens pull requests.
|
|
10
|
-
All of that is delegated to the agent via ``@agent-directive:``
|
|
11
|
-
markers per
|
|
12
|
-
``agents/contexts/implement-ticket-flow.md#agent-directives``. The
|
|
13
|
-
agent executes the directive, writes the resulting slice back to
|
|
14
|
-
the state file, and re-invokes this script to resume.
|
|
15
|
-
|
|
16
|
-
Exit codes:
|
|
17
|
-
|
|
18
|
-
- ``0`` — flow reached SUCCESS; ``state.report`` printed.
|
|
19
|
-
- ``1`` — flow halted BLOCKED; halt surface printed on stdout, the
|
|
20
|
-
state file carries the updated ``outcomes`` and ``questions`` so
|
|
21
|
-
the agent can resume.
|
|
22
|
-
- ``2`` — argument or I/O error (ticket file missing, JSON parse
|
|
23
|
-
failure, etc.). The state file is *not* written in this case.
|
|
24
|
-
"""
|
|
25
|
-
from __future__ import annotations
|
|
26
|
-
|
|
27
|
-
import argparse
|
|
28
|
-
import json
|
|
29
|
-
import sys
|
|
30
|
-
from dataclasses import asdict
|
|
31
|
-
from pathlib import Path
|
|
32
|
-
from typing import Sequence
|
|
33
|
-
|
|
34
|
-
from .delivery_state import DeliveryState, Outcome
|
|
35
|
-
from .dispatcher import dispatch
|
|
36
|
-
from .steps import analyze, implement, memory, plan, refine, report, verify
|
|
37
|
-
from .steps import test as test_step
|
|
38
|
-
|
|
39
|
-
DEFAULT_STATE_FILE = Path(".implement-ticket-state.json")
|
|
40
|
-
"""State file used when ``--state-file`` is not passed."""
|
|
41
|
-
|
|
42
|
-
_STEPS = {
|
|
43
|
-
"refine": refine.run,
|
|
44
|
-
"memory": memory.run,
|
|
45
|
-
"analyze": analyze.run,
|
|
46
|
-
"plan": plan.run,
|
|
47
|
-
"implement": implement.run,
|
|
48
|
-
"test": test_step.run,
|
|
49
|
-
"verify": verify.run,
|
|
50
|
-
"report": report.run,
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def main(argv: Sequence[str] | None = None) -> int:
|
|
55
|
-
"""Run one dispatch cycle against the persisted state.
|
|
56
|
-
|
|
57
|
-
``argv`` is taken as-is; pass ``None`` to fall back to
|
|
58
|
-
``sys.argv[1:]`` (the usual entry-point contract).
|
|
59
|
-
"""
|
|
60
|
-
parser = _build_parser()
|
|
61
|
-
args = parser.parse_args(argv)
|
|
62
|
-
state_file: Path = args.state_file
|
|
63
|
-
|
|
64
|
-
try:
|
|
65
|
-
state = _load_or_build(state_file, args)
|
|
66
|
-
except _CLIError as exc:
|
|
67
|
-
print(f"error: {exc}", file=sys.stderr)
|
|
68
|
-
return 2
|
|
69
|
-
|
|
70
|
-
final, halting = dispatch(state, _STEPS)
|
|
71
|
-
_save(state_file, state)
|
|
72
|
-
_emit(state, final, halting)
|
|
73
|
-
return 0 if final is Outcome.SUCCESS else 1
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def _build_parser() -> argparse.ArgumentParser:
|
|
77
|
-
parser = argparse.ArgumentParser(
|
|
78
|
-
prog="implement-ticket",
|
|
79
|
-
description="Run one dispatch cycle of the /implement-ticket flow.",
|
|
80
|
-
)
|
|
81
|
-
parser.add_argument(
|
|
82
|
-
"--state-file",
|
|
83
|
-
type=Path,
|
|
84
|
-
default=DEFAULT_STATE_FILE,
|
|
85
|
-
help=f"Path to persisted state JSON (default: {DEFAULT_STATE_FILE}).",
|
|
86
|
-
)
|
|
87
|
-
parser.add_argument(
|
|
88
|
-
"--ticket-file",
|
|
89
|
-
type=Path,
|
|
90
|
-
default=None,
|
|
91
|
-
help="JSON file carrying the ticket payload; used only when the "
|
|
92
|
-
"state file does not exist yet.",
|
|
93
|
-
)
|
|
94
|
-
parser.add_argument(
|
|
95
|
-
"--persona",
|
|
96
|
-
type=str,
|
|
97
|
-
default=None,
|
|
98
|
-
help="Persona name (senior-engineer | qa | advisory). Only honoured "
|
|
99
|
-
"when the state file does not exist yet; ignored on resume so a "
|
|
100
|
-
"mid-flight persona switch cannot silently change behaviour.",
|
|
101
|
-
)
|
|
102
|
-
return parser
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def _load_or_build(state_file: Path, args: argparse.Namespace) -> DeliveryState:
|
|
106
|
-
"""Return the state to dispatch against — either loaded or freshly built."""
|
|
107
|
-
if state_file.exists():
|
|
108
|
-
return _load(state_file)
|
|
109
|
-
if args.ticket_file is None:
|
|
110
|
-
raise _CLIError(
|
|
111
|
-
f"No state file at {state_file} and no --ticket-file given; "
|
|
112
|
-
"cannot build an initial DeliveryState.",
|
|
113
|
-
)
|
|
114
|
-
ticket = _read_json(args.ticket_file)
|
|
115
|
-
if not isinstance(ticket, dict):
|
|
116
|
-
raise _CLIError(
|
|
117
|
-
f"--ticket-file must carry a JSON object; got {type(ticket).__name__}.",
|
|
118
|
-
)
|
|
119
|
-
kwargs: dict = {"ticket": ticket}
|
|
120
|
-
if args.persona:
|
|
121
|
-
kwargs["persona"] = args.persona
|
|
122
|
-
return DeliveryState(**kwargs)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def _load(state_file: Path) -> DeliveryState:
|
|
126
|
-
data = _read_json(state_file)
|
|
127
|
-
if not isinstance(data, dict):
|
|
128
|
-
raise _CLIError(
|
|
129
|
-
f"State file {state_file} must carry a JSON object; "
|
|
130
|
-
f"got {type(data).__name__}.",
|
|
131
|
-
)
|
|
132
|
-
try:
|
|
133
|
-
return DeliveryState(**data)
|
|
134
|
-
except TypeError as exc:
|
|
135
|
-
raise _CLIError(f"State file shape is invalid: {exc}") from exc
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def _save(state_file: Path, state: DeliveryState) -> None:
|
|
139
|
-
"""Persist ``state`` as pretty JSON for diffing + human inspection."""
|
|
140
|
-
state_file.parent.mkdir(parents=True, exist_ok=True)
|
|
141
|
-
state_file.write_text(
|
|
142
|
-
json.dumps(asdict(state), indent=2, ensure_ascii=False) + "\n",
|
|
143
|
-
encoding="utf-8",
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def _read_json(path: Path):
|
|
148
|
-
try:
|
|
149
|
-
raw = path.read_text(encoding="utf-8")
|
|
150
|
-
except OSError as exc:
|
|
151
|
-
raise _CLIError(f"Cannot read {path}: {exc}") from exc
|
|
152
|
-
try:
|
|
153
|
-
return json.loads(raw)
|
|
154
|
-
except json.JSONDecodeError as exc:
|
|
155
|
-
raise _CLIError(f"Invalid JSON in {path}: {exc}") from exc
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def _emit(state: DeliveryState, final: Outcome, halting: str | None) -> None:
|
|
159
|
-
if final is Outcome.SUCCESS:
|
|
160
|
-
print(state.report)
|
|
161
|
-
return
|
|
162
|
-
print(f"[halt] outcome={final.value} step={halting or '(none)'}")
|
|
163
|
-
for line in state.questions:
|
|
164
|
-
print(line)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
class _CLIError(Exception):
|
|
168
|
-
"""Raised on configuration or I/O problems. Converted to exit code 2."""
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
__all__ = ["DEFAULT_STATE_FILE", "main"]
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
"""Linear step dispatcher for ``/implement-ticket``.
|
|
2
|
-
|
|
3
|
-
The dispatcher holds no business logic. It walks the fixed eight-step
|
|
4
|
-
order declared in ``agents/contexts/implement-ticket-flow.md``, hands
|
|
5
|
-
each step a live ``DeliveryState``, and honours the three terminal
|
|
6
|
-
outcomes:
|
|
7
|
-
|
|
8
|
-
- ``SUCCESS`` — record and advance.
|
|
9
|
-
- ``BLOCKED`` — record, copy questions onto the state, halt.
|
|
10
|
-
- ``PARTIAL`` — record, copy questions onto the state, halt.
|
|
11
|
-
|
|
12
|
-
Resumption semantics (Option A, flow contract §agent-directives):
|
|
13
|
-
steps whose name is already marked ``success`` in
|
|
14
|
-
``state.outcomes`` are **skipped**. This lets a caller re-invoke the
|
|
15
|
-
dispatcher after executing an agent-directive (the ``implement``,
|
|
16
|
-
``test``, ``verify`` steps cannot run from pure Python), update the
|
|
17
|
-
relevant slice of ``DeliveryState``, record ``success`` on the
|
|
18
|
-
resumed step, and continue without replaying earlier work.
|
|
19
|
-
|
|
20
|
-
Step handlers are injected by the caller rather than discovered at
|
|
21
|
-
import time. Phase 1 shipped the dispatcher with mock handlers;
|
|
22
|
-
Phase 2 wires the real ones under ``steps/``. Keeping injection
|
|
23
|
-
explicit means the dispatcher is trivially testable and never
|
|
24
|
-
depends on handler import order.
|
|
25
|
-
"""
|
|
26
|
-
from __future__ import annotations
|
|
27
|
-
|
|
28
|
-
from collections.abc import Mapping
|
|
29
|
-
|
|
30
|
-
from .delivery_state import DeliveryState, Outcome, Step, StepResult
|
|
31
|
-
|
|
32
|
-
STEP_ORDER: tuple[str, ...] = (
|
|
33
|
-
"refine",
|
|
34
|
-
"memory",
|
|
35
|
-
"analyze",
|
|
36
|
-
"plan",
|
|
37
|
-
"implement",
|
|
38
|
-
"test",
|
|
39
|
-
"verify",
|
|
40
|
-
"report",
|
|
41
|
-
)
|
|
42
|
-
"""Canonical execution order. Eight steps, fixed, no branching.
|
|
43
|
-
|
|
44
|
-
Changing this order is a roadmap-level decision — not a PR rider — per
|
|
45
|
-
the surface-growth guardrails in
|
|
46
|
-
``agents/roadmaps/road-to-implement-ticket.md``.
|
|
47
|
-
"""
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def dispatch(
|
|
51
|
-
state: DeliveryState,
|
|
52
|
-
steps: Mapping[str, Step],
|
|
53
|
-
) -> tuple[Outcome, str | None]:
|
|
54
|
-
"""Run the eight steps linearly against ``state``.
|
|
55
|
-
|
|
56
|
-
Returns a ``(final_outcome, halting_step)`` tuple. ``halting_step``
|
|
57
|
-
is ``None`` when every step succeeded; otherwise it carries the
|
|
58
|
-
name of the step whose result halted the flow.
|
|
59
|
-
|
|
60
|
-
Parameters
|
|
61
|
-
----------
|
|
62
|
-
state:
|
|
63
|
-
Live ``DeliveryState``. Mutated in place: each step's outcome
|
|
64
|
-
is recorded in ``state.outcomes`` under the step name, and
|
|
65
|
-
any surfaced questions land on ``state.questions``.
|
|
66
|
-
steps:
|
|
67
|
-
Mapping from step name to handler. Every entry in
|
|
68
|
-
:data:`STEP_ORDER` must be present; missing entries raise
|
|
69
|
-
``KeyError`` at dispatch time rather than silently skipping,
|
|
70
|
-
so incomplete wiring surfaces as a hard failure.
|
|
71
|
-
|
|
72
|
-
Raises
|
|
73
|
-
------
|
|
74
|
-
KeyError
|
|
75
|
-
If ``steps`` does not cover every entry in
|
|
76
|
-
:data:`STEP_ORDER`.
|
|
77
|
-
"""
|
|
78
|
-
_assert_all_steps_present(steps)
|
|
79
|
-
|
|
80
|
-
# Clear stale questions from a previous halt before we resume so
|
|
81
|
-
# the caller never mistakes old options for fresh ones.
|
|
82
|
-
state.questions = []
|
|
83
|
-
|
|
84
|
-
for name in STEP_ORDER:
|
|
85
|
-
if state.outcomes.get(name) == Outcome.SUCCESS.value:
|
|
86
|
-
# Already completed on an earlier invocation — skip per the
|
|
87
|
-
# resume contract. The caller is responsible for keeping
|
|
88
|
-
# ``state.outcomes`` and the matching slice in sync.
|
|
89
|
-
continue
|
|
90
|
-
|
|
91
|
-
handler = steps[name]
|
|
92
|
-
result = handler(state)
|
|
93
|
-
_validate_step_result(name, result)
|
|
94
|
-
|
|
95
|
-
state.outcomes[name] = result.outcome.value
|
|
96
|
-
|
|
97
|
-
if result.outcome is Outcome.BLOCKED:
|
|
98
|
-
state.questions = list(result.questions)
|
|
99
|
-
return Outcome.BLOCKED, name
|
|
100
|
-
|
|
101
|
-
if result.outcome is Outcome.PARTIAL:
|
|
102
|
-
state.questions = list(result.questions)
|
|
103
|
-
return Outcome.PARTIAL, name
|
|
104
|
-
|
|
105
|
-
return Outcome.SUCCESS, None
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def _assert_all_steps_present(steps: Mapping[str, Step]) -> None:
|
|
109
|
-
"""Reject an incomplete step mapping up front.
|
|
110
|
-
|
|
111
|
-
We deliberately fail loudly here: a missing step would otherwise
|
|
112
|
-
raise deep inside the dispatch loop after partial state mutation,
|
|
113
|
-
which makes debugging the wiring harder than it needs to be.
|
|
114
|
-
"""
|
|
115
|
-
missing = [name for name in STEP_ORDER if name not in steps]
|
|
116
|
-
if missing:
|
|
117
|
-
raise KeyError(
|
|
118
|
-
"Step mapping is missing handlers for: " + ", ".join(missing),
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def _validate_step_result(name: str, result: StepResult) -> None:
|
|
123
|
-
"""Enforce the blocked/partial invariant: questions must be set.
|
|
124
|
-
|
|
125
|
-
A step that blocks without surfacing a question is a bug — there
|
|
126
|
-
is nothing for the user to answer. We raise ``ValueError`` instead
|
|
127
|
-
of silently recording the outcome so the defect is visible at the
|
|
128
|
-
earliest possible point.
|
|
129
|
-
"""
|
|
130
|
-
if result.outcome in (Outcome.BLOCKED, Outcome.PARTIAL) and not result.questions:
|
|
131
|
-
raise ValueError(
|
|
132
|
-
f"Step {name!r} returned {result.outcome.value} with no questions; "
|
|
133
|
-
"blocked and partial outcomes must surface at least one numbered option.",
|
|
134
|
-
)
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"""Step handlers for the ``/implement-ticket`` dispatcher.
|
|
2
|
-
|
|
3
|
-
Each module exposes a single ``run`` callable that matches the
|
|
4
|
-
``Step`` protocol defined in ``..delivery_state``. The dispatcher
|
|
5
|
-
wires them into the ``STEP_ORDER`` mapping at call time; nothing in
|
|
6
|
-
this package imports handlers eagerly, so a partial wiring during
|
|
7
|
-
development is caught by the dispatcher's missing-step check rather
|
|
8
|
-
than by an import-time failure.
|
|
9
|
-
|
|
10
|
-
Phase 2 ships all eight step handlers. The deterministic gates
|
|
11
|
-
(``refine``, ``memory``, ``analyze``) validate upstream state; the
|
|
12
|
-
delegation gates (``plan``, ``implement``, ``test``, ``verify``)
|
|
13
|
-
halt with ``@agent-directive:`` markers so the orchestrator can
|
|
14
|
-
invoke the matching skill and resume. ``report`` renders the
|
|
15
|
-
delivery Markdown once everything else has succeeded. See
|
|
16
|
-
``agents/roadmaps/road-to-implement-ticket.md`` for the shipping
|
|
17
|
-
order and ``agents/contexts/implement-ticket-flow.md`` for the
|
|
18
|
-
slice contracts each handler writes to.
|
|
19
|
-
"""
|
|
20
|
-
from __future__ import annotations
|
|
21
|
-
|
|
22
|
-
from . import analyze, implement, memory, plan, refine, report, test, verify
|
|
23
|
-
|
|
24
|
-
_STEPS = (refine, memory, analyze, plan, implement, test, verify, report)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def all_ambiguities() -> dict[str, tuple[dict[str, str], ...]]:
|
|
28
|
-
"""Return `{step_name: AMBIGUITIES}` for every step in flow order.
|
|
29
|
-
|
|
30
|
-
Used by documentation generators and the ``test_ambiguity_coverage``
|
|
31
|
-
suite to prove every step explicitly declares what can surface a
|
|
32
|
-
``BLOCKED`` outcome. Steps that always succeed (``memory``,
|
|
33
|
-
``report``) return an empty tuple — declared intent, not an
|
|
34
|
-
omission.
|
|
35
|
-
"""
|
|
36
|
-
return {step.__name__.rsplit(".", 1)[-1]: step.AMBIGUITIES for step in _STEPS}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
__all__ = [
|
|
40
|
-
"all_ambiguities",
|
|
41
|
-
"analyze",
|
|
42
|
-
"implement",
|
|
43
|
-
"memory",
|
|
44
|
-
"plan",
|
|
45
|
-
"refine",
|
|
46
|
-
"report",
|
|
47
|
-
"test",
|
|
48
|
-
"verify",
|
|
49
|
-
]
|