@event4u/agent-config 1.14.0 → 1.16.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 +1 -1
- package/.agent-src/commands/bug-fix.md +3 -3
- package/.agent-src/commands/bug-investigate.md +2 -2
- package/.agent-src/commands/chat-history-checkpoint.md +3 -3
- package/.agent-src/commands/chat-history-clear.md +2 -2
- package/.agent-src/commands/chat-history-resume.md +2 -2
- package/.agent-src/commands/chat-history.md +3 -3
- package/.agent-src/commands/check-current-md.md +44 -33
- package/.agent-src/commands/commit-in-chunks.md +43 -23
- package/.agent-src/commands/compress.md +34 -2
- package/.agent-src/commands/council-design.md +96 -0
- package/.agent-src/commands/council-optimize.md +115 -0
- package/.agent-src/commands/council-pr.md +123 -0
- package/.agent-src/commands/council.md +219 -0
- package/.agent-src/commands/create-pr.md +23 -0
- package/.agent-src/commands/do-and-judge.md +3 -3
- package/.agent-src/commands/do-in-steps.md +4 -4
- package/.agent-src/commands/e2e-heal.md +1 -1
- package/.agent-src/commands/e2e-plan.md +1 -1
- package/.agent-src/commands/feature-dev.md +8 -0
- package/.agent-src/commands/feature-explore.md +6 -1
- package/.agent-src/commands/feature-plan.md +33 -2
- package/.agent-src/commands/feature-refactor.md +5 -0
- package/.agent-src/commands/feature-roadmap.md +8 -3
- package/.agent-src/commands/feature.md +58 -0
- package/.agent-src/commands/fix-ci.md +5 -0
- package/.agent-src/commands/fix-portability.md +7 -2
- package/.agent-src/commands/fix-pr-bot-comments.md +5 -0
- package/.agent-src/commands/fix-pr-comments.md +5 -0
- package/.agent-src/commands/fix-pr-developer-comments.md +5 -0
- package/.agent-src/commands/fix-references.md +5 -0
- package/.agent-src/commands/fix-seeder.md +5 -0
- package/.agent-src/commands/fix.md +60 -0
- package/.agent-src/commands/jira-ticket.md +1 -1
- package/.agent-src/commands/judge.md +1 -1
- package/.agent-src/commands/memory-add.md +3 -3
- package/.agent-src/commands/memory-full.md +2 -2
- package/.agent-src/commands/memory-promote.md +2 -2
- package/.agent-src/commands/mode.md +5 -5
- package/.agent-src/commands/onboard.md +17 -8
- package/.agent-src/commands/optimize-agents.md +6 -1
- package/.agent-src/commands/optimize-augmentignore.md +14 -0
- package/.agent-src/commands/optimize-rtk-filters.md +5 -0
- package/.agent-src/commands/optimize-skills.md +6 -1
- package/.agent-src/commands/optimize.md +54 -0
- package/.agent-src/commands/propose-memory.md +2 -2
- package/.agent-src/commands/refine-ticket.md +9 -7
- package/.agent-src/commands/review-changes.md +61 -9
- package/.agent-src/commands/review-routing.md +1 -1
- package/.agent-src/commands/roadmap-create.md +42 -4
- package/.agent-src/commands/roadmap-execute.md +9 -7
- package/.agent-src/commands/set-cost-profile.md +11 -3
- package/.agent-src/commands/sync-agent-settings.md +11 -2
- package/.agent-src/commands/tests-create.md +1 -1
- package/.agent-src/commands/tests-execute.md +2 -3
- package/.agent-src/commands/upstream-contribute.md +1 -1
- package/.agent-src/contexts/authority/commit-mechanics.md +57 -0
- package/.agent-src/contexts/authority/destructive-mechanics.md +66 -0
- package/.agent-src/contexts/authority/scope-mechanics.md +87 -0
- package/.agent-src/contexts/execution/autonomy-detection.md +54 -0
- package/.agent-src/contexts/execution/autonomy-examples.md +90 -0
- package/.agent-src/contexts/execution/autonomy-mechanics.md +29 -0
- package/.agent-src/contexts/execution/verification-mechanics.md +80 -0
- package/.agent-src/personas/README.md +1 -1
- package/.agent-src/rules/agent-authority.md +24 -0
- package/.agent-src/rules/architecture.md +1 -1
- package/.agent-src/rules/artifact-drafting-protocol.md +1 -1
- package/.agent-src/rules/artifact-engagement-recording.md +2 -2
- package/.agent-src/rules/ask-when-uncertain.md +1 -1
- package/.agent-src/rules/augment-portability.md +56 -37
- package/.agent-src/rules/autonomous-execution.md +78 -114
- package/.agent-src/rules/capture-learnings.md +1 -1
- 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 +1 -1
- package/.agent-src/rules/{command-suggestion.md → command-suggestion-policy.md} +10 -9
- package/.agent-src/rules/commit-conventions.md +1 -1
- package/.agent-src/rules/commit-policy.md +43 -61
- package/.agent-src/rules/context-hygiene.md +3 -3
- package/.agent-src/rules/direct-answers.md +2 -2
- package/.agent-src/rules/docs-sync.md +1 -1
- package/.agent-src/rules/e2e-testing.md +1 -1
- package/.agent-src/rules/guidelines.md +4 -4
- package/.agent-src/rules/improve-before-implement.md +2 -2
- package/.agent-src/rules/language-and-tone.md +41 -96
- package/.agent-src/rules/minimal-safe-diff.md +3 -3
- package/.agent-src/rules/model-recommendation.md +4 -4
- package/.agent-src/rules/no-cheap-questions.md +89 -0
- package/.agent-src/rules/non-destructive-by-default.md +25 -59
- package/.agent-src/rules/onboarding-gate.md +5 -5
- package/.agent-src/rules/review-routing-awareness.md +9 -9
- package/.agent-src/rules/roadmap-progress-sync.md +132 -80
- package/.agent-src/rules/role-mode-adherence.md +3 -3
- package/.agent-src/rules/scope-control.md +65 -46
- package/.agent-src/rules/security-sensitive-stop.md +2 -2
- package/.agent-src/rules/size-enforcement.md +3 -2
- package/.agent-src/rules/think-before-action.md +5 -5
- package/.agent-src/rules/token-efficiency.md +4 -4
- package/.agent-src/rules/{ui-audit-before-build.md → ui-audit-gate.md} +3 -3
- package/.agent-src/rules/user-interaction.md +31 -7
- package/.agent-src/rules/verify-before-complete.md +12 -67
- package/.agent-src/scripts/update_roadmap_progress.py +65 -8
- package/.agent-src/skills/ai-council/SKILL.md +333 -0
- package/.agent-src/skills/api-endpoint/SKILL.md +2 -2
- package/.agent-src/skills/blade-ui/SKILL.md +30 -11
- package/.agent-src/skills/blast-radius-analyzer/SKILL.md +1 -1
- package/.agent-src/skills/bug-analyzer/SKILL.md +1 -1
- package/.agent-src/skills/command-routing/SKILL.md +1 -1
- package/.agent-src/skills/command-writing/SKILL.md +16 -5
- package/.agent-src/skills/conventional-commits-writing/SKILL.md +1 -1
- package/.agent-src/skills/copilot-agents-optimization/SKILL.md +2 -2
- package/.agent-src/skills/developer-like-execution/SKILL.md +2 -2
- package/.agent-src/skills/existing-ui-audit/SKILL.md +24 -9
- package/.agent-src/skills/fe-design/SKILL.md +20 -15
- package/.agent-src/skills/file-editor/SKILL.md +9 -0
- package/.agent-src/skills/flux/SKILL.md +1 -1
- package/.agent-src/skills/git-workflow/SKILL.md +1 -1
- package/.agent-src/skills/guideline-writing/SKILL.md +11 -11
- package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +4 -4
- package/.agent-src/skills/livewire/SKILL.md +27 -8
- package/.agent-src/skills/override-management/SKILL.md +2 -2
- package/.agent-src/skills/php-coder/SKILL.md +1 -1
- package/.agent-src/skills/playwright-testing/SKILL.md +2 -2
- package/.agent-src/skills/readme-reviewer/SKILL.md +1 -1
- package/.agent-src/skills/readme-writing/SKILL.md +1 -1
- package/.agent-src/skills/readme-writing-package/SKILL.md +1 -1
- package/.agent-src/skills/receiving-code-review/SKILL.md +1 -1
- package/.agent-src/skills/refine-ticket/SKILL.md +30 -24
- package/.agent-src/skills/review-routing/SKILL.md +2 -2
- package/.agent-src/skills/roadmap-management/SKILL.md +22 -16
- package/.agent-src/skills/rule-writing/SKILL.md +1 -1
- package/.agent-src/skills/skill-reviewer/SKILL.md +1 -1
- package/.agent-src/skills/skill-writing/SKILL.md +6 -6
- package/.agent-src/skills/subagent-orchestration/SKILL.md +1 -0
- package/.agent-src/skills/systematic-debugging/SKILL.md +1 -1
- package/.agent-src/skills/upstream-contribute/SKILL.md +3 -3
- package/.agent-src/skills/validate-feature-fit/SKILL.md +2 -2
- package/.agent-src/skills/{verify-before-complete → verify-completion-evidence}/SKILL.md +2 -2
- package/.agent-src/templates/agent-settings.md +9 -9
- package/.agent-src/templates/contexts/auth-model.md +1 -1
- package/.agent-src/templates/roadmaps.md +9 -8
- package/.agent-src/templates/scripts/README.md +2 -2
- package/.agent-src/templates/scripts/memory_lookup.py +1 -1
- package/.agent-src/templates/scripts/telemetry/aggregator.py +16 -1
- package/.agent-src/templates/scripts/telemetry/engagement.py +59 -0
- package/.agent-src/templates/scripts/telemetry/report_renderer.py +28 -1
- package/.agent-src/templates/scripts/telemetry_record.py +14 -1
- package/.agent-src/templates/scripts/work_engine/__init__.py +2 -2
- package/.agent-src/templates/scripts/work_engine/cli.py +64 -461
- package/.agent-src/templates/scripts/work_engine/cli_args.py +116 -0
- package/.agent-src/templates/scripts/work_engine/delivery_state.py +3 -3
- package/.agent-src/templates/scripts/work_engine/directives/backend/__init__.py +1 -1
- package/.agent-src/templates/scripts/work_engine/directives/backend/implement.py +1 -1
- package/.agent-src/templates/scripts/work_engine/directives/backend/memory.py +1 -1
- package/.agent-src/templates/scripts/work_engine/directives/backend/plan.py +1 -1
- package/.agent-src/templates/scripts/work_engine/directives/backend/report.py +1 -1
- package/.agent-src/templates/scripts/work_engine/dispatcher.py +1 -1
- 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/input_builders.py +163 -0
- package/.agent-src/templates/scripts/work_engine/migration/v0_to_v1.py +34 -2
- package/.agent-src/templates/scripts/work_engine/persona_policy.py +1 -1
- package/.agent-src/templates/scripts/work_engine/resolvers/prompt.py +1 -1
- package/.agent-src/templates/scripts/work_engine/state_io.py +202 -0
- package/.claude-plugin/marketplace.json +10 -2
- package/AGENTS.md +16 -12
- package/CHANGELOG.md +206 -9
- package/README.md +51 -52
- package/config/agent-settings.template.yml +58 -1
- package/config/gitignore-block.txt +3 -0
- package/docs/MIGRATION.md +122 -0
- package/docs/architecture.md +83 -34
- package/docs/catalog.md +331 -0
- package/docs/contracts/STABILITY.md +134 -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/load-context-schema.md +186 -0
- package/docs/contracts/rule-interactions.md +107 -0
- package/docs/contracts/rule-interactions.yml +238 -0
- package/docs/contracts/rule-priority-hierarchy.md +87 -0
- package/docs/contracts/ui-stack-extension.md +236 -0
- package/docs/contracts/ui-track-flow.md +338 -0
- package/docs/customization.md +14 -0
- package/docs/end-to-end-walkthroughs.md +165 -0
- package/docs/getting-started.md +27 -9
- package/docs/github-topics.md +12 -3
- package/docs/guidelines/agent-infra/language-and-tone-examples.md +79 -0
- package/{.agent-src → docs}/guidelines/docs/readme-size-and-splitting.md +26 -25
- package/docs/guidelines/php/git.md +164 -0
- package/docs/installation.md +42 -6
- package/docs/migrations/commands-1.15.0.md +112 -0
- package/docs/showcase.md +9 -4
- package/docs/skills-catalog.md +14 -8
- package/docs/ui-track-mental-model.md +121 -0
- package/llms.txt +13 -7
- package/package.json +1 -1
- package/scripts/agent-config +23 -0
- package/scripts/ai_council/__init__.py +39 -0
- package/scripts/ai_council/_default_prices.py +41 -0
- package/scripts/ai_council/_one_off_rebalancing_audit.py +149 -0
- package/scripts/ai_council/_one_off_roundtrip.py +106 -0
- package/scripts/ai_council/budget_guard.py +172 -0
- package/scripts/ai_council/bundler.py +261 -0
- package/scripts/ai_council/clients.py +381 -0
- package/scripts/ai_council/modes.py +127 -0
- package/scripts/ai_council/orchestrator.py +350 -0
- package/scripts/ai_council/pricing.py +213 -0
- package/scripts/ai_council/project_context.py +159 -0
- package/scripts/ai_council/prompts.py +232 -0
- package/scripts/ai_council/session.py +144 -0
- package/scripts/build_linear_digest.py +4 -4
- package/scripts/check_always_budget.py +126 -0
- package/scripts/check_augmentignore.py +69 -0
- package/scripts/check_command_count_messaging.py +120 -0
- package/scripts/check_portability.py +57 -0
- package/scripts/check_public_catalog_links.py +122 -0
- package/scripts/check_public_links.py +185 -0
- package/scripts/check_references.py +5 -1
- package/scripts/check_roadmap_trackable.py +111 -0
- package/scripts/command_suggester/cooldown.py +1 -1
- package/scripts/generate_index.py +266 -0
- package/scripts/install_anthropic_key.sh +5 -0
- package/scripts/install_openai_key.sh +106 -0
- package/scripts/lint_load_context.py +163 -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/release.py +297 -64
- package/scripts/schemas/command.schema.json +20 -0
- package/scripts/schemas/rule.schema.json +10 -0
- package/scripts/skill_linter.py +26 -4
- package/scripts/sync_agent_settings.py +1 -1
- package/scripts/update_counts.py +19 -4
- package/scripts/update_prices.py +124 -0
- package/.agent-src/guidelines/php/git.md +0 -96
- package/.agent-src/rules/chat-history.md +0 -200
- /package/.agent-src/rules/{slash-commands.md → slash-command-routing-policy.md} +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/agent-interaction-and-decision-quality.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/break-glass-usage.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/developer-judgment.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/engineering-memory-data-format.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/layered-settings.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/memory-access.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/naming.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/output-patterns.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/review-routing-data-format.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/role-contracts.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/role-mode-router.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/runtime-layer.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/self-improvement-pipeline.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/size-and-scope.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/tool-integration.md +0 -0
- /package/{.agent-src → docs}/guidelines/e2e/playwright.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/api-design.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/artisan-commands.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/blade-ui.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/controllers.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/database.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/eloquent.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/flux.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/general.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/jobs.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/livewire.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/logging.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/naming.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/dependency-injection.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/dtos.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/events.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/factory.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/pipelines.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/policies.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/repositories.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/service-layer.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/strategy.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/performance.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/resources.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/security.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/sql.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/validations.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/websocket.md +0 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Atomic-command linter for the command-collapse policy.
|
|
4
|
+
|
|
5
|
+
Reads the locked verb clusters from `docs/contracts/command-clusters.md`,
|
|
6
|
+
finds every command file under `.agent-src.uncompressed/commands/` that
|
|
7
|
+
was **added** since `--baseline` (default: `main`), and requires each
|
|
8
|
+
new file to declare either:
|
|
9
|
+
|
|
10
|
+
- `cluster: <locked-name>` (file is a cluster entry or sub-command), or
|
|
11
|
+
- `superseded_by: <slug>` (file is a deprecation shim).
|
|
12
|
+
|
|
13
|
+
Modifications to pre-existing files are NOT flagged — only additions.
|
|
14
|
+
This stops the atomic surface from growing without forcing every existing
|
|
15
|
+
command into a Phase 1 cluster (most aren't in Phase 1).
|
|
16
|
+
|
|
17
|
+
Exit codes: 0 = clean, 1 = violations found, 3 = internal error.
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
python3 scripts/lint_no_new_atomic_commands.py
|
|
21
|
+
python3 scripts/lint_no_new_atomic_commands.py --baseline origin/main
|
|
22
|
+
python3 scripts/lint_no_new_atomic_commands.py --all # ignore baseline
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import argparse
|
|
28
|
+
import re
|
|
29
|
+
import subprocess
|
|
30
|
+
import sys
|
|
31
|
+
from dataclasses import dataclass
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
|
|
34
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
35
|
+
COMMANDS_DIR = Path(".agent-src.uncompressed/commands")
|
|
36
|
+
CLUSTER_CONTRACT = Path("docs/contracts/command-clusters.md")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class Violation:
|
|
41
|
+
file: str
|
|
42
|
+
reason: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def load_locked_clusters() -> set[str]:
|
|
46
|
+
"""Parse the Phase 1 cluster table from the locked contract."""
|
|
47
|
+
text = (ROOT / CLUSTER_CONTRACT).read_text(encoding="utf-8")
|
|
48
|
+
# Locate the Phase 1 table; cluster names sit in backticks in column 1.
|
|
49
|
+
in_phase_1 = False
|
|
50
|
+
clusters: set[str] = set()
|
|
51
|
+
for line in text.splitlines():
|
|
52
|
+
if line.startswith("## Phase 1 clusters"):
|
|
53
|
+
in_phase_1 = True
|
|
54
|
+
continue
|
|
55
|
+
if in_phase_1 and line.startswith("## "):
|
|
56
|
+
break
|
|
57
|
+
if in_phase_1:
|
|
58
|
+
m = re.match(r"\|\s*`([a-z][a-z0-9-]*)`\s*\|", line)
|
|
59
|
+
if m:
|
|
60
|
+
clusters.add(m.group(1))
|
|
61
|
+
if not clusters:
|
|
62
|
+
print(
|
|
63
|
+
f"❌ Could not parse Phase 1 cluster table from {CLUSTER_CONTRACT}",
|
|
64
|
+
file=sys.stderr,
|
|
65
|
+
)
|
|
66
|
+
sys.exit(3)
|
|
67
|
+
return clusters
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def added_command_files(baseline: str) -> list[Path]:
|
|
71
|
+
"""Files under commands/ added (status A) since baseline."""
|
|
72
|
+
try:
|
|
73
|
+
result = subprocess.run(
|
|
74
|
+
["git", "diff", "--name-only", "--diff-filter=A",
|
|
75
|
+
f"{baseline}...HEAD", "--", str(COMMANDS_DIR)],
|
|
76
|
+
capture_output=True, text=True, cwd=ROOT, timeout=15,
|
|
77
|
+
)
|
|
78
|
+
except (FileNotFoundError, subprocess.TimeoutExpired) as exc:
|
|
79
|
+
print(f"❌ git diff failed: {exc}", file=sys.stderr)
|
|
80
|
+
sys.exit(3)
|
|
81
|
+
if result.returncode != 0:
|
|
82
|
+
print(f"❌ git diff exit {result.returncode}: {result.stderr}",
|
|
83
|
+
file=sys.stderr)
|
|
84
|
+
sys.exit(3)
|
|
85
|
+
files = [Path(p) for p in result.stdout.splitlines()
|
|
86
|
+
if p.endswith(".md") and p != ""]
|
|
87
|
+
# Also include untracked (newly added, uncommitted) files.
|
|
88
|
+
try:
|
|
89
|
+
wt = subprocess.run(
|
|
90
|
+
["git", "status", "--porcelain", "--", str(COMMANDS_DIR)],
|
|
91
|
+
capture_output=True, text=True, cwd=ROOT, timeout=10,
|
|
92
|
+
)
|
|
93
|
+
for line in wt.stdout.splitlines():
|
|
94
|
+
if len(line) < 4:
|
|
95
|
+
continue
|
|
96
|
+
status = line[:2]
|
|
97
|
+
if status.strip() not in ("A", "??", "AM"):
|
|
98
|
+
continue
|
|
99
|
+
path = line[3:].strip().split(" -> ")[-1]
|
|
100
|
+
if path.endswith(".md"):
|
|
101
|
+
p = Path(path)
|
|
102
|
+
if p not in files:
|
|
103
|
+
files.append(p)
|
|
104
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
105
|
+
pass
|
|
106
|
+
return files
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def all_command_files() -> list[Path]:
|
|
110
|
+
return sorted((ROOT / COMMANDS_DIR).glob("*.md"))
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def parse_frontmatter(path: Path) -> dict[str, str]:
|
|
114
|
+
text = path.read_text(encoding="utf-8")
|
|
115
|
+
if not text.startswith("---"):
|
|
116
|
+
return {}
|
|
117
|
+
end = text.find("\n---", 3)
|
|
118
|
+
if end == -1:
|
|
119
|
+
return {}
|
|
120
|
+
fm: dict[str, str] = {}
|
|
121
|
+
for line in text[3:end].splitlines():
|
|
122
|
+
if ":" in line:
|
|
123
|
+
k, _, v = line.partition(":")
|
|
124
|
+
fm[k.strip()] = v.strip()
|
|
125
|
+
return fm
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def check_file(path: Path, clusters: set[str]) -> Violation | None:
|
|
129
|
+
abs_path = path if path.is_absolute() else ROOT / path
|
|
130
|
+
if not abs_path.exists():
|
|
131
|
+
return None # deleted file, nothing to check
|
|
132
|
+
fm = parse_frontmatter(abs_path)
|
|
133
|
+
if "superseded_by" in fm:
|
|
134
|
+
return None # shim — exempt
|
|
135
|
+
cluster = fm.get("cluster")
|
|
136
|
+
if not cluster:
|
|
137
|
+
return Violation(str(path),
|
|
138
|
+
"missing `cluster:` frontmatter "
|
|
139
|
+
f"(allowed: {sorted(clusters)})")
|
|
140
|
+
if cluster not in clusters:
|
|
141
|
+
return Violation(str(path),
|
|
142
|
+
f"`cluster: {cluster}` is not a locked cluster "
|
|
143
|
+
f"(allowed: {sorted(clusters)})")
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def main() -> int:
|
|
148
|
+
ap = argparse.ArgumentParser(description=__doc__)
|
|
149
|
+
ap.add_argument("--baseline", default="main",
|
|
150
|
+
help="git ref to diff against (default: main)")
|
|
151
|
+
ap.add_argument("--all", action="store_true",
|
|
152
|
+
help="check every command file, not just changed ones")
|
|
153
|
+
args = ap.parse_args()
|
|
154
|
+
|
|
155
|
+
clusters = load_locked_clusters()
|
|
156
|
+
targets = (all_command_files() if args.all
|
|
157
|
+
else added_command_files(args.baseline))
|
|
158
|
+
if not targets:
|
|
159
|
+
print(f"✅ No new commands added under {COMMANDS_DIR} "
|
|
160
|
+
f"(baseline: {args.baseline}).")
|
|
161
|
+
return 0
|
|
162
|
+
|
|
163
|
+
violations = [v for v in (check_file(p, clusters) for p in targets)
|
|
164
|
+
if v is not None]
|
|
165
|
+
if violations:
|
|
166
|
+
print(f"❌ {len(violations)} newly-added atomic command(s) violate "
|
|
167
|
+
f"the command-cluster policy:")
|
|
168
|
+
for v in violations:
|
|
169
|
+
print(f" • {v.file} — {v.reason}")
|
|
170
|
+
print(f"\nSee docs/contracts/command-clusters.md for the locked "
|
|
171
|
+
f"cluster names and frontmatter contract.")
|
|
172
|
+
return 1
|
|
173
|
+
print(f"✅ {len(targets)} newly-added command(s) all declare a valid "
|
|
174
|
+
f"`cluster:` (or `superseded_by:`).")
|
|
175
|
+
return 0
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
if __name__ == "__main__":
|
|
179
|
+
sys.exit(main())
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Lint docs/contracts/rule-interactions.yml.
|
|
3
|
+
|
|
4
|
+
Validates:
|
|
5
|
+
- Schema (required fields per pair)
|
|
6
|
+
- All rule slugs in `rules:` exist as `.agent-src.uncompressed/rules/<slug>.md`
|
|
7
|
+
- Every pair references rules listed in the top-level `rules:` block
|
|
8
|
+
- `relation` is one of the allowed values
|
|
9
|
+
- All `evidence:` entries point at real files (anchors are advisory, not checked)
|
|
10
|
+
- Pair `id`s are unique
|
|
11
|
+
- The anchor pair from `road-to-post-pr29-optimize.md` Phase 2 is present:
|
|
12
|
+
`non-destructive-by-default` × {autonomous-execution, scope-control,
|
|
13
|
+
commit-policy, ask-when-uncertain, verify-before-complete}.
|
|
14
|
+
|
|
15
|
+
Exits non-zero on any failure. Used in CI via Taskfile target
|
|
16
|
+
`lint-rule-interactions`.
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
import yaml
|
|
24
|
+
|
|
25
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
26
|
+
MATRIX = ROOT / "docs" / "contracts" / "rule-interactions.yml"
|
|
27
|
+
RULES_DIR = ROOT / ".agent-src.uncompressed" / "rules"
|
|
28
|
+
|
|
29
|
+
ALLOWED_RELATIONS = {
|
|
30
|
+
"overrides",
|
|
31
|
+
"narrows",
|
|
32
|
+
"defers_to",
|
|
33
|
+
"restates",
|
|
34
|
+
"gates",
|
|
35
|
+
"complements",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
REQUIRED_PAIR_FIELDS = {"id", "rules", "relation", "conflict", "resolution", "evidence"}
|
|
39
|
+
|
|
40
|
+
ANCHOR_PARTNERS = {
|
|
41
|
+
"autonomous-execution",
|
|
42
|
+
"scope-control",
|
|
43
|
+
"commit-policy",
|
|
44
|
+
"ask-when-uncertain",
|
|
45
|
+
"verify-before-complete",
|
|
46
|
+
}
|
|
47
|
+
ANCHOR_RULE = "non-destructive-by-default"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def fail(errors: list[str]) -> None:
|
|
51
|
+
print(f"❌ rule-interactions.yml — {len(errors)} issue(s):")
|
|
52
|
+
for e in errors:
|
|
53
|
+
print(f" • {e}")
|
|
54
|
+
sys.exit(1)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def main() -> int:
|
|
58
|
+
if not MATRIX.exists():
|
|
59
|
+
fail([f"{MATRIX.relative_to(ROOT)} is missing"])
|
|
60
|
+
|
|
61
|
+
data = yaml.safe_load(MATRIX.read_text())
|
|
62
|
+
errors: list[str] = []
|
|
63
|
+
|
|
64
|
+
if not isinstance(data, dict):
|
|
65
|
+
fail(["top-level YAML must be a mapping"])
|
|
66
|
+
|
|
67
|
+
if data.get("version") != 1:
|
|
68
|
+
errors.append("version must be 1")
|
|
69
|
+
|
|
70
|
+
declared_rules = data.get("rules") or []
|
|
71
|
+
if not isinstance(declared_rules, list) or not declared_rules:
|
|
72
|
+
errors.append("`rules:` must be a non-empty list of slugs")
|
|
73
|
+
|
|
74
|
+
for slug in declared_rules:
|
|
75
|
+
if not isinstance(slug, str):
|
|
76
|
+
errors.append(f"rule slug not a string: {slug!r}")
|
|
77
|
+
continue
|
|
78
|
+
rule_path = RULES_DIR / f"{slug}.md"
|
|
79
|
+
if not rule_path.exists():
|
|
80
|
+
errors.append(f"rule slug `{slug}` has no file at {rule_path.relative_to(ROOT)}")
|
|
81
|
+
|
|
82
|
+
pairs = data.get("pairs") or []
|
|
83
|
+
if not isinstance(pairs, list) or not pairs:
|
|
84
|
+
errors.append("`pairs:` must be a non-empty list")
|
|
85
|
+
|
|
86
|
+
seen_ids: set[str] = set()
|
|
87
|
+
declared_set = set(declared_rules) if isinstance(declared_rules, list) else set()
|
|
88
|
+
anchor_partners_seen: set[str] = set()
|
|
89
|
+
|
|
90
|
+
for idx, pair in enumerate(pairs):
|
|
91
|
+
if not isinstance(pair, dict):
|
|
92
|
+
errors.append(f"pair[{idx}] is not a mapping")
|
|
93
|
+
continue
|
|
94
|
+
missing = REQUIRED_PAIR_FIELDS - set(pair)
|
|
95
|
+
if missing:
|
|
96
|
+
errors.append(f"pair[{idx}] missing fields: {sorted(missing)}")
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
pid = pair["id"]
|
|
100
|
+
if pid in seen_ids:
|
|
101
|
+
errors.append(f"duplicate pair id: {pid}")
|
|
102
|
+
seen_ids.add(pid)
|
|
103
|
+
|
|
104
|
+
rules_pair = pair["rules"]
|
|
105
|
+
if not (isinstance(rules_pair, list) and len(rules_pair) == 2):
|
|
106
|
+
errors.append(f"pair `{pid}` rules must be a 2-element list")
|
|
107
|
+
continue
|
|
108
|
+
for r in rules_pair:
|
|
109
|
+
if r not in declared_set:
|
|
110
|
+
errors.append(f"pair `{pid}` references undeclared rule `{r}`")
|
|
111
|
+
|
|
112
|
+
if pair["relation"] not in ALLOWED_RELATIONS:
|
|
113
|
+
errors.append(
|
|
114
|
+
f"pair `{pid}` relation `{pair['relation']}` not in {sorted(ALLOWED_RELATIONS)}"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
evidence = pair.get("evidence") or []
|
|
118
|
+
if not isinstance(evidence, list) or not evidence:
|
|
119
|
+
errors.append(f"pair `{pid}` evidence must be a non-empty list")
|
|
120
|
+
for citation in evidence:
|
|
121
|
+
if not isinstance(citation, str):
|
|
122
|
+
errors.append(f"pair `{pid}` evidence item not a string: {citation!r}")
|
|
123
|
+
continue
|
|
124
|
+
file_part = citation.split("#", 1)[0]
|
|
125
|
+
if not (ROOT / file_part).exists():
|
|
126
|
+
errors.append(f"pair `{pid}` evidence path does not exist: {file_part}")
|
|
127
|
+
|
|
128
|
+
# Anchor coverage check
|
|
129
|
+
if ANCHOR_RULE in rules_pair:
|
|
130
|
+
partner = next((r for r in rules_pair if r != ANCHOR_RULE), None)
|
|
131
|
+
if partner in ANCHOR_PARTNERS:
|
|
132
|
+
anchor_partners_seen.add(partner)
|
|
133
|
+
|
|
134
|
+
missing_anchors = ANCHOR_PARTNERS - anchor_partners_seen
|
|
135
|
+
if missing_anchors:
|
|
136
|
+
errors.append(
|
|
137
|
+
f"anchor pairs missing for `{ANCHOR_RULE}` × {sorted(missing_anchors)} "
|
|
138
|
+
"(required by road-to-post-pr29-optimize.md P2.2)"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if errors:
|
|
142
|
+
fail(errors)
|
|
143
|
+
|
|
144
|
+
print(f"✅ rule-interactions.yml clean — {len(declared_rules)} rules, {len(pairs)} pairs.")
|
|
145
|
+
return 0
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if __name__ == "__main__":
|
|
149
|
+
sys.exit(main())
|
package/scripts/memory_lookup.py
CHANGED
|
@@ -246,7 +246,7 @@ def _apply_conflict_rule(
|
|
|
246
246
|
# says retrieval should route through `@event4u/agent-memory`. The package
|
|
247
247
|
# CLI is purely **semantic** (`memory retrieve <query> --type T …`); the
|
|
248
248
|
# shared `retrieve(types, keys, …)` API is **key-based**. The hybrid
|
|
249
|
-
# resolution agreed in `
|
|
249
|
+
# resolution agreed in `docs/contracts/agent-memory-contract.md` synthesises
|
|
250
250
|
# `keys` into a single natural-language query for the package call, while
|
|
251
251
|
# the file fallback continues to do glob/substring matching on the same
|
|
252
252
|
# keys. Both legs land in the same `Hit` shape so the conflict rule can
|