@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
|
@@ -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
|
|
package/scripts/update_counts.py
CHANGED
|
@@ -72,6 +72,16 @@ TARGETS: list[tuple[str, list[tuple[str, str]]]] = [
|
|
|
72
72
|
(r"(Browse all )(\d+)( commands\])", "commands"),
|
|
73
73
|
],
|
|
74
74
|
),
|
|
75
|
+
(
|
|
76
|
+
"docs/architecture.md",
|
|
77
|
+
[
|
|
78
|
+
# "What's inside" table: | **Skills** | NNN | … |
|
|
79
|
+
(r"(\| \*\*Skills\*\* \| )(\d+)( \|)", "skills"),
|
|
80
|
+
(r"(\| \*\*Rules\*\* \| )(\d+)( \|)", "rules"),
|
|
81
|
+
(r"(\| \*\*Commands\*\* \| )(\d+)( \|)", "commands"),
|
|
82
|
+
(r"(\| \*\*Guidelines\*\* \| )(\d+)( \|)", "guidelines"),
|
|
83
|
+
],
|
|
84
|
+
),
|
|
75
85
|
# Note: ``agents/roadmaps/road-to-stronger-skills.md`` was previously
|
|
76
86
|
# tracked here with a living ``baseline N as of`` pattern. The roadmap
|
|
77
87
|
# was moved to ``skipped/`` on 2026-04-23 (Q35 superseded), so its
|
|
@@ -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
|
-
---
|
|
2
|
-
type: "always"
|
|
3
|
-
description: "Persist the conversation to .agent-chat-history for crash recovery — read on first turn, detect match/returning/foreign/missing, append on progress, honor per-profile frequency and overflow settings"
|
|
4
|
-
alwaysApply: true
|
|
5
|
-
source: package
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Chat History
|
|
9
|
-
|
|
10
|
-
Persists the conversation to `.agent-chat-history` (JSONL, project root,
|
|
11
|
-
git-ignored) so a crashed or switched agent session can be resumed. File
|
|
12
|
-
I/O is owned by [`scripts/chat_history.py`](../../../scripts/chat_history.py)
|
|
13
|
-
— this rule says **when** to call it, not how the file is structured.
|
|
14
|
-
|
|
15
|
-
## Activation
|
|
16
|
-
|
|
17
|
-
Read `chat_history.*` from `.agent-settings.yml` **once per conversation**
|
|
18
|
-
(first turn). Cache the values.
|
|
19
|
-
|
|
20
|
-
- `chat_history.enabled: false` **or** section missing → rule is a **no-op**
|
|
21
|
-
for the whole conversation. Do not read, write, or mention the file.
|
|
22
|
-
- `chat_history.enabled: true` → cache `frequency`, `max_size_kb`,
|
|
23
|
-
`on_overflow` and proceed to the first-turn handshake.
|
|
24
|
-
|
|
25
|
-
## First-turn handshake — four states
|
|
26
|
-
|
|
27
|
-
Before executing the user's request, run:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
scripts/chat_history.py state --first-user-msg "<msg>"
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
It prints exactly one of `match` | `returning` | `foreign` | `missing`.
|
|
34
|
-
Branch:
|
|
35
|
-
|
|
36
|
-
- `missing` → `init --first-user-msg "<msg>" --freq <frequency>`. Proceed
|
|
37
|
-
silently.
|
|
38
|
-
- `match` → this chat already owns the file. Continue appending as cached.
|
|
39
|
-
- `foreign` → a different session's file. Show **Foreign-Prompt** below.
|
|
40
|
-
- `returning` → this chat once owned the file but another session took
|
|
41
|
-
over. Show **Returning-Prompt** below.
|
|
42
|
-
|
|
43
|
-
In `foreign` and `returning`, **always read the file's current contents
|
|
44
|
-
into the agent's working context before any write** — the user chose to
|
|
45
|
-
log history for a reason; losing it silently is never acceptable.
|
|
46
|
-
|
|
47
|
-
## Foreign-Prompt — new chat finds existing history
|
|
48
|
-
|
|
49
|
-
Trigger: `state == foreign` **and** `status.entries >= 1`.
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
> 📒 Found chat history from an unknown session.
|
|
53
|
-
>
|
|
54
|
-
> Header fingerprint: <short-hash-A>
|
|
55
|
-
> Current session: <short-hash-B>
|
|
56
|
-
> Entries on file: <N> Age: <age>
|
|
57
|
-
>
|
|
58
|
-
> 1. Resume — adopt this file, load entries as context, keep appending here
|
|
59
|
-
> 2. New start — archive to .agent-chat-history.bak, init fresh
|
|
60
|
-
> 3. Ignore — leave the file untouched, disable logging for this session
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
- `1` → `adopt --first-user-msg "<msg>"` (the old fp lands in
|
|
64
|
-
`former_fps` automatically). Read entries into context, then append
|
|
65
|
-
normally.
|
|
66
|
-
- `2` → rename to `.agent-chat-history.bak`, then
|
|
67
|
-
`init --first-user-msg "<msg>" --freq <frequency>`.
|
|
68
|
-
- `3` → logging disabled for this conversation; do not touch the file,
|
|
69
|
-
do not edit settings.
|
|
70
|
-
|
|
71
|
-
Free-text replies ("weiter", "skip it") count as `3`.
|
|
72
|
-
|
|
73
|
-
## Returning-Prompt — old chat comes back
|
|
74
|
-
|
|
75
|
-
Trigger: `state == returning`. The file exists, its current owner is a
|
|
76
|
-
different session, but this chat's fingerprint is in `former_fps`. The
|
|
77
|
-
agent still has its own in-memory history of the turns it logged before
|
|
78
|
-
the hand-off.
|
|
79
|
-
|
|
80
|
-
```
|
|
81
|
-
> 📒 Welcome back. This chat once owned the history file; another
|
|
82
|
-
> session has written to it since.
|
|
83
|
-
>
|
|
84
|
-
> On-file entries: <N> Size: <X> KB (now includes <M> foreign entries)
|
|
85
|
-
>
|
|
86
|
-
> (All three options read the on-disk entries into context first.)
|
|
87
|
-
>
|
|
88
|
-
> 1. Merge — my in-memory history first, the foreign entries after,
|
|
89
|
-
> overwrite the file with the combined body
|
|
90
|
-
> 2. Replace — wipe the foreign entries, rewrite the file with my
|
|
91
|
-
> in-memory history only
|
|
92
|
-
> 3. Continue — leave the file as-is; only new entries from now on
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
- `1` (Merge) → build the in-memory entries list (see below), call
|
|
96
|
-
`prepend --entries-json '<list>'`, then `adopt --first-user-msg "<msg>"`.
|
|
97
|
-
- `2` (Replace) → build the in-memory list,
|
|
98
|
-
`reset --first-user-msg "<msg>" --freq <frequency> --entries-json '<list>'`.
|
|
99
|
-
- `3` (Continue) → `adopt --first-user-msg "<msg>"`, then append normally.
|
|
100
|
-
|
|
101
|
-
Free-text replies count as `3`.
|
|
102
|
-
|
|
103
|
-
## Building the in-memory entries list (Merge / Replace)
|
|
104
|
-
|
|
105
|
-
The agent reconstructs its own conversation as a JSON array, one entry
|
|
106
|
-
per turn boundary. Keep it compact:
|
|
107
|
-
|
|
108
|
-
- One `{"t":"user","text":"<preview>","ts":"<iso>"}` per user message.
|
|
109
|
-
- One `{"t":"agent","text":"<preview>","ts":"<iso>"}` per agent reply.
|
|
110
|
-
- `text` is a preview — flatten whitespace, cap at ~200 characters. This
|
|
111
|
-
is context, not a transcript.
|
|
112
|
-
- Timestamps in ISO-8601 UTC. If the agent does not have exact times,
|
|
113
|
-
use the current time for all entries; order is what matters.
|
|
114
|
-
- Do **not** include tool-call payloads, file contents, or secrets.
|
|
115
|
-
|
|
116
|
-
If the list is large (>30 KB), pass it via stdin:
|
|
117
|
-
`reset ... --entries-stdin <<< '<list>'`.
|
|
118
|
-
|
|
119
|
-
## Append cadence — from `frequency`
|
|
120
|
-
|
|
121
|
-
Every append goes through `scripts/chat_history.py append --type <t>
|
|
122
|
-
--json '<obj>'`. Never write the file directly.
|
|
123
|
-
|
|
124
|
-
- `per_turn` — one entry at the end of every agent turn.
|
|
125
|
-
- `per_phase` — at phase boundaries (user question, agent answer,
|
|
126
|
-
decision, completion of a task-list item).
|
|
127
|
-
- `per_tool` — after each tool-call sequence.
|
|
128
|
-
|
|
129
|
-
Entry types: `user`, `agent`, `tool`, `decision`, `phase`. Prefer `phase`
|
|
130
|
-
over `agent` when the entry marks a boundary.
|
|
131
|
-
|
|
132
|
-
## Overflow — from `on_overflow`
|
|
133
|
-
|
|
134
|
-
When the helper reports file size > `max_size_kb`:
|
|
135
|
-
|
|
136
|
-
- `rotate` → `rotate --mode rotate --max-kb <n>`. Drops oldest entries;
|
|
137
|
-
silent and cheap.
|
|
138
|
-
- `compress` → `rotate --mode compress --max-kb <n>`. Marks the file for
|
|
139
|
-
summarization; the **next** turn writes the summary for the dropped
|
|
140
|
-
range. Do not block the current turn on this.
|
|
141
|
-
|
|
142
|
-
After Merge or Replace rewrites, run the overflow check once — the new
|
|
143
|
-
body may exceed the budget.
|
|
144
|
-
|
|
145
|
-
The setting is stable for the session; never mix modes.
|
|
146
|
-
|
|
147
|
-
## What this rule does NOT do
|
|
148
|
-
|
|
149
|
-
- Display, reload, or clear the log — that is `/chat-history`,
|
|
150
|
-
`/chat-history-resume`, `/chat-history-clear`.
|
|
151
|
-
- Auto-flip `enabled` or `on_overflow` in settings.
|
|
152
|
-
- Run when `enabled: false`. No silent logging. No telemetry.
|
|
153
|
-
- Decide ownership heuristically. Only the `state` helper does that.
|
|
154
|
-
|
|
155
|
-
## Interactions
|
|
156
|
-
|
|
157
|
-
- `ask-when-uncertain` + `user-interaction` — foreign/returning prompts
|
|
158
|
-
use numbered options, one question per turn.
|
|
159
|
-
- `language-and-tone` — prompt translated at runtime; `.md` stays English.
|
|
160
|
-
- `onboarding-gate` — runs first; this rule activates only after it clears.
|
|
161
|
-
- `token-efficiency` — never load the full log into context from this
|
|
162
|
-
rule; use `status` for metadata, `read --last N` for a tail.
|
|
163
|
-
|
|
164
|
-
## See also
|
|
165
|
-
|
|
166
|
-
- [`scripts/chat_history.py`](../../../scripts/chat_history.py) — file API
|
|
167
|
-
- [`/chat-history`](../commands/chat-history.md) — status inspection
|
|
168
|
-
- [`/chat-history-resume`](../commands/chat-history-resume.md) — adopt + load
|
|
169
|
-
- [`/chat-history-clear`](../commands/chat-history-clear.md) — wipe
|
|
170
|
-
- [`agent-settings` template](../templates/agent-settings.md) — `chat_history.*` reference
|
|
171
|
-
- [`rule-type-governance`](rule-type-governance.md) — why this is `always`
|
|
@@ -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"]
|