@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,69 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Advisory check for `.augmentignore` (road-to-governance-cleanup F6.3).
|
|
4
|
+
|
|
5
|
+
Runs as part of `task ci` to surface `/optimize augmentignore` as a
|
|
6
|
+
periodic reminder. Always exits 0 — this is a warn-only advisory, not
|
|
7
|
+
a gate. Failures here never block CI.
|
|
8
|
+
|
|
9
|
+
Checks performed:
|
|
10
|
+
1. Does `.augmentignore` exist at repo root?
|
|
11
|
+
2. Is its mtime older than 90 days? (stale reminder)
|
|
12
|
+
3. Is it suspiciously short (<5 non-blank, non-comment lines)?
|
|
13
|
+
|
|
14
|
+
If any check trips, prints a friendly hint and exits 0. If all clean,
|
|
15
|
+
prints a single-line OK and exits 0.
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import sys
|
|
20
|
+
import time
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
STALE_DAYS = 90
|
|
24
|
+
MIN_USEFUL_LINES = 5
|
|
25
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def check() -> int:
|
|
29
|
+
target = REPO_ROOT / ".augmentignore"
|
|
30
|
+
notes: list[str] = []
|
|
31
|
+
|
|
32
|
+
if not target.exists():
|
|
33
|
+
notes.append("⚠️ .augmentignore is missing — run `/optimize augmentignore` to scaffold it.")
|
|
34
|
+
_emit(notes)
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
age_days = (time.time() - target.stat().st_mtime) / 86400
|
|
38
|
+
if age_days > STALE_DAYS:
|
|
39
|
+
notes.append(
|
|
40
|
+
f"ℹ️ .augmentignore is {int(age_days)} days old (threshold: {STALE_DAYS}) — "
|
|
41
|
+
"consider running `/optimize augmentignore` to refresh."
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
useful = [
|
|
45
|
+
ln for ln in target.read_text().splitlines()
|
|
46
|
+
if ln.strip() and not ln.strip().startswith("#")
|
|
47
|
+
]
|
|
48
|
+
if len(useful) < MIN_USEFUL_LINES:
|
|
49
|
+
notes.append(
|
|
50
|
+
f"ℹ️ .augmentignore has only {len(useful)} active entries — "
|
|
51
|
+
"run `/optimize augmentignore` to detect tech-stack ignores you may be missing."
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
_emit(notes)
|
|
55
|
+
return 0
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _emit(notes: list[str]) -> None:
|
|
59
|
+
if not notes:
|
|
60
|
+
print("✅ .augmentignore advisory: nothing to suggest.")
|
|
61
|
+
return
|
|
62
|
+
print("📒 .augmentignore advisory (non-blocking):")
|
|
63
|
+
for n in notes:
|
|
64
|
+
print(f" {n}")
|
|
65
|
+
print(" (This is a reminder, not a CI failure.)")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
sys.exit(check())
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Command-count messaging gate (regression guard for road-to-pr-34-followups 1.2).
|
|
4
|
+
|
|
5
|
+
Public surfaces (README.md, AGENTS.md, docs/getting-started.md) advertise
|
|
6
|
+
the size of the command catalog. PR #34 collapses atomic commands into
|
|
7
|
+
clusters via deprecation shims — the externally meaningful number is the
|
|
8
|
+
**active** command count (non-shim files), not the raw file count. This
|
|
9
|
+
gate sources canonical counts from `.agent-src.uncompressed/commands/`
|
|
10
|
+
frontmatter and fails when any documented number drifts from those.
|
|
11
|
+
|
|
12
|
+
Canonical counts:
|
|
13
|
+
total = number of *.md files under .agent-src.uncompressed/commands/
|
|
14
|
+
shims = files whose frontmatter declares `superseded_by:`
|
|
15
|
+
active = total - shims
|
|
16
|
+
|
|
17
|
+
Patterns checked (per file):
|
|
18
|
+
|
|
19
|
+
README.md
|
|
20
|
+
hero row "<strong>{N} Commands</strong>" → active
|
|
21
|
+
browse line "Browse all {N} active commands" → active
|
|
22
|
+
browse meta "{N} files total" → total
|
|
23
|
+
browse meta "{N} are deprecation shims" → shims
|
|
24
|
+
tools blurb "{N} native commands" → active
|
|
25
|
+
|
|
26
|
+
AGENTS.md
|
|
27
|
+
tree "commands/ ({N} files — {A} active + {S} deprecation shims)"
|
|
28
|
+
|
|
29
|
+
docs/getting-started.md
|
|
30
|
+
browse line "Browse all {N} active commands" → active
|
|
31
|
+
|
|
32
|
+
Exit codes: 0 = clean, 1 = drift detected.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
import re
|
|
38
|
+
import sys
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
|
|
41
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
42
|
+
COMMANDS_DIR = ROOT / ".agent-src.uncompressed" / "commands"
|
|
43
|
+
README = ROOT / "README.md"
|
|
44
|
+
AGENTS = ROOT / "AGENTS.md"
|
|
45
|
+
GETTING_STARTED = ROOT / "docs" / "getting-started.md"
|
|
46
|
+
|
|
47
|
+
FM_RE = re.compile(r"^---\s*\n(.*?)\n---", re.DOTALL)
|
|
48
|
+
SUPERSEDED_RE = re.compile(r"^superseded_by:\s*\S", re.MULTILINE)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def canonical_counts() -> tuple[int, int, int]:
|
|
52
|
+
if not COMMANDS_DIR.is_dir():
|
|
53
|
+
print(f"❌ {COMMANDS_DIR.relative_to(ROOT)} not found", file=sys.stderr)
|
|
54
|
+
sys.exit(1)
|
|
55
|
+
total = shims = 0
|
|
56
|
+
for f in COMMANDS_DIR.glob("*.md"):
|
|
57
|
+
total += 1
|
|
58
|
+
m = FM_RE.match(f.read_text(encoding="utf-8"))
|
|
59
|
+
fm = m.group(1) if m else ""
|
|
60
|
+
if SUPERSEDED_RE.search(fm):
|
|
61
|
+
shims += 1
|
|
62
|
+
return total, shims, total - shims
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _check(path: Path, pattern: str, expected: int, label: str) -> str | None:
|
|
66
|
+
if not path.exists():
|
|
67
|
+
return f"missing file: {path.relative_to(ROOT)}"
|
|
68
|
+
m = re.search(pattern, path.read_text(encoding="utf-8"))
|
|
69
|
+
if not m:
|
|
70
|
+
return f"{path.relative_to(ROOT)}: pattern not found for `{label}` — /{pattern}/"
|
|
71
|
+
found = int(m.group(1))
|
|
72
|
+
if found != expected:
|
|
73
|
+
return f"{path.relative_to(ROOT)}: `{label}` says {found}, expected {expected}"
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def main() -> int:
|
|
78
|
+
total, shims, active = canonical_counts()
|
|
79
|
+
print(f"Canonical counts: {total} files · {shims} shims · {active} active")
|
|
80
|
+
|
|
81
|
+
checks = [
|
|
82
|
+
# README.md
|
|
83
|
+
(README, r"<strong>(\d+) Commands</strong>", active, "hero row"),
|
|
84
|
+
(README, r"Browse all (\d+) active commands", active, "browse line"),
|
|
85
|
+
(README, r"\((\d+) files total ", total, "browse meta · total files"),
|
|
86
|
+
(README, r"— (\d+) are deprecation shims", shims, "browse meta · shims"),
|
|
87
|
+
(README, r"\+ (\d+) native commands\)", active, "tools blurb"),
|
|
88
|
+
# AGENTS.md (`commands/ (84 files — 69 active + 15 deprecation shims)`)
|
|
89
|
+
(AGENTS, r"commands/\s+\((\d+) files —", total, "tree · total files"),
|
|
90
|
+
(AGENTS, r"files — (\d+) active", active, "tree · active"),
|
|
91
|
+
(AGENTS, r"active \+ (\d+) deprecation shims", shims, "tree · shims"),
|
|
92
|
+
# docs/getting-started.md
|
|
93
|
+
(GETTING_STARTED, r"Browse all (\d+) active commands", active, "browse line"),
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
errors: list[str] = []
|
|
97
|
+
for path, pattern, expected, label in checks:
|
|
98
|
+
err = _check(path, pattern, expected, label)
|
|
99
|
+
if err:
|
|
100
|
+
errors.append(err)
|
|
101
|
+
|
|
102
|
+
if not errors:
|
|
103
|
+
print("✅ All command-count messaging in sync with registry.")
|
|
104
|
+
return 0
|
|
105
|
+
|
|
106
|
+
print(f"❌ Command-count messaging drift — {len(errors)} mismatch(es):")
|
|
107
|
+
for e in errors:
|
|
108
|
+
print(f" {e}")
|
|
109
|
+
print(
|
|
110
|
+
"\nFix: update the documented numbers above, or run "
|
|
111
|
+
"`task check-command-count` after editing."
|
|
112
|
+
)
|
|
113
|
+
print(
|
|
114
|
+
"Why this gate exists: see `agents/roadmaps/road-to-pr-34-followups.md` § 1.2."
|
|
115
|
+
)
|
|
116
|
+
return 1
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
sys.exit(main())
|
|
@@ -157,6 +157,8 @@ ALLOWLIST = [
|
|
|
157
157
|
r"agent-config", # refers to the package concept, not a specific project
|
|
158
158
|
r"shared.*package", # "shared package" concept
|
|
159
159
|
r"package repository", # "package repository" concept
|
|
160
|
+
r"scripts/mcp_server/", # MCP server module path (road-to-mcp-server.md Phase 1)
|
|
161
|
+
r"scripts\.mcp_server", # MCP server Python module entrypoint
|
|
160
162
|
]
|
|
161
163
|
|
|
162
164
|
# Directories to scan (only package files, not project-specific agents/)
|
|
@@ -248,6 +250,55 @@ def check_file(filepath: Path, patterns: list, allowlist: list) -> List[Violatio
|
|
|
248
250
|
return violations
|
|
249
251
|
|
|
250
252
|
|
|
253
|
+
# ── Identity-framing detector ───────────────────────────────────────────
|
|
254
|
+
# The package's public identity surface (README, AGENTS, copilot-instructions)
|
|
255
|
+
# must read stack-neutral. Laravel is the deepest reference stack today, never
|
|
256
|
+
# the headline. This detector flags banned phrases that elevate any single
|
|
257
|
+
# stack to identity status. Source-of-truth list lives in the
|
|
258
|
+
# road-to-1-15-followups.md roadmap (P0 #1, F1.5).
|
|
259
|
+
_IDENTITY_FRAMING_PATTERNS: list[tuple[re.Pattern, str]] = [
|
|
260
|
+
(re.compile(r"\bLaravel-first\b", re.IGNORECASE), "identity-laravel-first"),
|
|
261
|
+
(re.compile(r"\bfor\s+PHP\s*/\s*Laravel\s+teams?\b", re.IGNORECASE), "identity-for-php-laravel-teams"),
|
|
262
|
+
(re.compile(r"\bfor\s+Laravel\s+teams?\b", re.IGNORECASE), "identity-for-laravel-teams"),
|
|
263
|
+
(re.compile(r"\bprimary\s+audience\s*[:=]\s*Laravel\b", re.IGNORECASE), "identity-primary-audience-laravel"),
|
|
264
|
+
(re.compile(r"\bbuilt\s+for\s+Laravel\b", re.IGNORECASE), "identity-built-for-laravel"),
|
|
265
|
+
(re.compile(r"\bLaravel\s*=\s*primary\b", re.IGNORECASE), "identity-laravel-equals-primary"),
|
|
266
|
+
(re.compile(r"\*\*Reference\s+implementation:\s*Laravel\.?\*\*", re.IGNORECASE), "identity-reference-implementation-laravel"),
|
|
267
|
+
]
|
|
268
|
+
|
|
269
|
+
# Files whose identity framing must stay stack-neutral. Relative to repo root.
|
|
270
|
+
IDENTITY_SCAN_FILES = [
|
|
271
|
+
"README.md",
|
|
272
|
+
"AGENTS.md",
|
|
273
|
+
".github/copilot-instructions.md",
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def check_identity_framing(filepath: Path) -> List[Violation]:
|
|
278
|
+
"""Flag banned identity-framing phrases in README / AGENTS / copilot-instructions.
|
|
279
|
+
|
|
280
|
+
The package presents itself as a universal governance system; any phrase
|
|
281
|
+
that pins identity to a single stack (Laravel-first, built for Laravel,
|
|
282
|
+
Reference implementation: Laravel as a bolded headline) is a regression.
|
|
283
|
+
"""
|
|
284
|
+
violations: List[Violation] = []
|
|
285
|
+
try:
|
|
286
|
+
lines = filepath.read_text(encoding="utf-8").splitlines()
|
|
287
|
+
except Exception:
|
|
288
|
+
return violations
|
|
289
|
+
|
|
290
|
+
for i, line in enumerate(lines, 1):
|
|
291
|
+
for pattern, name in _IDENTITY_FRAMING_PATTERNS:
|
|
292
|
+
m = pattern.search(line)
|
|
293
|
+
if m:
|
|
294
|
+
violations.append(Violation(
|
|
295
|
+
file=str(filepath), line=i, match=m.group(0),
|
|
296
|
+
pattern_name=name, severity="error",
|
|
297
|
+
context=line.strip(),
|
|
298
|
+
))
|
|
299
|
+
return violations
|
|
300
|
+
|
|
301
|
+
|
|
251
302
|
# ── Task-command detector ───────────────────────────────────────────────
|
|
252
303
|
# Artefact files shipped in the package must not reference `task <name>`
|
|
253
304
|
# invocations (per augment-portability rule). Consumer projects may not
|
|
@@ -465,6 +516,12 @@ def scan_all(root: Path) -> tuple[List[Violation], list[str]]:
|
|
|
465
516
|
if not any(path_str.endswith(skip) for skip in _CLI_DETECTOR_SKIP):
|
|
466
517
|
violations.extend(check_cli_invocations(f))
|
|
467
518
|
|
|
519
|
+
# Layer 5: identity-framing scan on the public identity surface
|
|
520
|
+
for rel in IDENTITY_SCAN_FILES:
|
|
521
|
+
f = root / rel
|
|
522
|
+
if f.is_file():
|
|
523
|
+
violations.extend(check_identity_framing(f))
|
|
524
|
+
|
|
468
525
|
return violations, detected
|
|
469
526
|
|
|
470
527
|
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Public-catalog link checker (regression guard for road-to-pr-34-followups 1.1).
|
|
4
|
+
|
|
5
|
+
`docs/catalog.md` is the consumer-facing catalog rendered by
|
|
6
|
+
`scripts/generate_index.py`. Consumers receive the package via npm /
|
|
7
|
+
Composer / archive surfaces — `.agent-src.uncompressed/` is **not**
|
|
8
|
+
shipped (see `package.json#files`). Every link in the public catalog
|
|
9
|
+
must therefore resolve to a shipped surface.
|
|
10
|
+
|
|
11
|
+
Checks:
|
|
12
|
+
1. No link href contains `.agent-src.uncompressed/`.
|
|
13
|
+
2. Every link href resolves on disk.
|
|
14
|
+
3. Every link href starts with a path declared in `package.json#files`
|
|
15
|
+
(or one of the always-shipped root files).
|
|
16
|
+
|
|
17
|
+
Exit codes: 0 = clean, 1 = violations found.
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
python3 scripts/check_public_catalog_links.py
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
import re
|
|
27
|
+
import sys
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
31
|
+
CATALOG = ROOT / "docs" / "catalog.md"
|
|
32
|
+
PACKAGE_JSON = ROOT / "package.json"
|
|
33
|
+
|
|
34
|
+
LINK_RE = re.compile(r"\]\((?P<href>[^)\s]+)(?:\s+\"[^\"]*\")?\)")
|
|
35
|
+
FORBIDDEN_PREFIX = ".agent-src.uncompressed/"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _shipped_roots() -> tuple[set[str], set[str]]:
|
|
39
|
+
"""Return (shipped_dirs, shipped_files) from package.json#files."""
|
|
40
|
+
data = json.loads(PACKAGE_JSON.read_text(encoding="utf-8"))
|
|
41
|
+
dirs: set[str] = set()
|
|
42
|
+
files: set[str] = set()
|
|
43
|
+
for entry in data.get("files", []):
|
|
44
|
+
if entry.endswith("/"):
|
|
45
|
+
dirs.add(entry.rstrip("/"))
|
|
46
|
+
else:
|
|
47
|
+
files.add(entry)
|
|
48
|
+
return dirs, files
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _resolve(href: str) -> Path | None:
|
|
52
|
+
href = href.split("#", 1)[0]
|
|
53
|
+
if not href or href.startswith(("http://", "https://", "mailto:", "tel:")):
|
|
54
|
+
return None
|
|
55
|
+
target = (CATALOG.parent / href).resolve()
|
|
56
|
+
try:
|
|
57
|
+
return target.relative_to(ROOT.resolve())
|
|
58
|
+
except ValueError:
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _under_shipped_surface(rel: Path, dirs: set[str], files: set[str]) -> bool:
|
|
63
|
+
s = str(rel)
|
|
64
|
+
if s in files:
|
|
65
|
+
return True
|
|
66
|
+
return any(s == d or s.startswith(d + "/") for d in dirs)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def main() -> int:
|
|
70
|
+
if not CATALOG.exists():
|
|
71
|
+
print(f"❌ {CATALOG.relative_to(ROOT)} not found")
|
|
72
|
+
return 1
|
|
73
|
+
|
|
74
|
+
dirs, files = _shipped_roots()
|
|
75
|
+
text = CATALOG.read_text(encoding="utf-8")
|
|
76
|
+
|
|
77
|
+
forbidden: list[tuple[int, str]] = []
|
|
78
|
+
missing: list[tuple[int, str]] = []
|
|
79
|
+
unshipped: list[tuple[int, str]] = []
|
|
80
|
+
|
|
81
|
+
for lineno, line in enumerate(text.splitlines(), 1):
|
|
82
|
+
for m in LINK_RE.finditer(line):
|
|
83
|
+
href = m.group("href")
|
|
84
|
+
if FORBIDDEN_PREFIX in href:
|
|
85
|
+
forbidden.append((lineno, href))
|
|
86
|
+
continue
|
|
87
|
+
rel = _resolve(href)
|
|
88
|
+
if rel is None:
|
|
89
|
+
continue # external / non-resolvable
|
|
90
|
+
if not (ROOT / rel).exists():
|
|
91
|
+
missing.append((lineno, href))
|
|
92
|
+
continue
|
|
93
|
+
if not _under_shipped_surface(rel, dirs, files):
|
|
94
|
+
unshipped.append((lineno, href))
|
|
95
|
+
|
|
96
|
+
total_violations = len(forbidden) + len(missing) + len(unshipped)
|
|
97
|
+
if not total_violations:
|
|
98
|
+
print(f"✅ docs/catalog.md — all links resolve to shipped surfaces.")
|
|
99
|
+
return 0
|
|
100
|
+
|
|
101
|
+
print(f"❌ docs/catalog.md — {total_violations} violation(s):")
|
|
102
|
+
if forbidden:
|
|
103
|
+
print(f"\n {len(forbidden)} link(s) point at unshipped `.agent-src.uncompressed/`:")
|
|
104
|
+
for ln, href in forbidden[:10]:
|
|
105
|
+
print(f" line {ln}: {href}")
|
|
106
|
+
if len(forbidden) > 10:
|
|
107
|
+
print(f" … and {len(forbidden) - 10} more")
|
|
108
|
+
if missing:
|
|
109
|
+
print(f"\n {len(missing)} link(s) do not resolve on disk:")
|
|
110
|
+
for ln, href in missing[:10]:
|
|
111
|
+
print(f" line {ln}: {href}")
|
|
112
|
+
if unshipped:
|
|
113
|
+
print(f"\n {len(unshipped)} link(s) point outside `package.json#files`:")
|
|
114
|
+
for ln, href in unshipped[:10]:
|
|
115
|
+
print(f" line {ln}: {href}")
|
|
116
|
+
print("\nFix: update `scripts/generate_index.py` _to_shipped_path() / catalog renderer,")
|
|
117
|
+
print("then re-run `python3 scripts/generate_index.py`.")
|
|
118
|
+
return 1
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if __name__ == "__main__":
|
|
122
|
+
sys.exit(main())
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Public-link checker for the agent-config public surface.
|
|
4
|
+
|
|
5
|
+
Scans the public-surface files (README.md, AGENTS.md, docs/architecture.md)
|
|
6
|
+
for markdown links into `docs/contracts/`, then validates each link against
|
|
7
|
+
the `stability:` frontmatter declared by the target file (per
|
|
8
|
+
`docs/contracts/STABILITY.md`).
|
|
9
|
+
|
|
10
|
+
Rules:
|
|
11
|
+
- target stability=stable → OK (no marker required).
|
|
12
|
+
- target stability=beta → OK; warns if surrounding text has no
|
|
13
|
+
visible "(beta)" marker.
|
|
14
|
+
- target stability=experimental → ERROR. Public surface MUST NOT link
|
|
15
|
+
to experimental contracts.
|
|
16
|
+
- target outside docs/contracts/ but referenced for contract-shaped
|
|
17
|
+
intent (links into agents/contexts/*.md from public files) → ERROR.
|
|
18
|
+
- target file missing → ERROR.
|
|
19
|
+
- target file under docs/contracts/ without `stability:` frontmatter
|
|
20
|
+
(except STABILITY.md itself) → ERROR.
|
|
21
|
+
|
|
22
|
+
Exit codes: 0 = clean, 1 = violations found, 3 = internal error.
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
python3 scripts/check_public_links.py
|
|
26
|
+
python3 scripts/check_public_links.py --list # list contracts + levels
|
|
27
|
+
python3 scripts/check_public_links.py --json # machine-readable
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import argparse
|
|
33
|
+
import json
|
|
34
|
+
import re
|
|
35
|
+
import sys
|
|
36
|
+
from dataclasses import dataclass, asdict
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
|
|
39
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
40
|
+
PUBLIC_FILES = [Path("README.md"), Path("AGENTS.md"), Path("docs/architecture.md")]
|
|
41
|
+
CONTRACTS_DIR = Path("docs/contracts")
|
|
42
|
+
STABILITY_FILE = CONTRACTS_DIR / "STABILITY.md"
|
|
43
|
+
|
|
44
|
+
LINK_RE = re.compile(r"\[(?P<text>[^\]]+)\]\((?P<href>[^)\s]+)(?:\s+\"[^\"]*\")?\)")
|
|
45
|
+
FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
|
|
46
|
+
STABILITY_RE = re.compile(r"^stability:\s*(\w+)\s*$", re.MULTILINE)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class Violation:
|
|
51
|
+
file: str
|
|
52
|
+
line: int
|
|
53
|
+
href: str
|
|
54
|
+
reason: str
|
|
55
|
+
severity: str # "error" | "warning"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def read_stability(path: Path) -> str | None:
|
|
59
|
+
if not path.exists():
|
|
60
|
+
return None
|
|
61
|
+
txt = path.read_text(encoding="utf-8")
|
|
62
|
+
m = FRONTMATTER_RE.match(txt)
|
|
63
|
+
if not m:
|
|
64
|
+
return None
|
|
65
|
+
sm = STABILITY_RE.search(m.group(1))
|
|
66
|
+
return sm.group(1) if sm else None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def collect_contracts() -> dict[Path, str | None]:
|
|
70
|
+
out: dict[Path, str | None] = {}
|
|
71
|
+
for p in sorted((ROOT / CONTRACTS_DIR).glob("*.md")):
|
|
72
|
+
rel = p.relative_to(ROOT)
|
|
73
|
+
out[rel] = read_stability(p)
|
|
74
|
+
return out
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def resolve(public_file: Path, href: str) -> Path | None:
|
|
78
|
+
href = href.split("#", 1)[0]
|
|
79
|
+
if not href or href.startswith(("http://", "https://", "mailto:", "tel:")):
|
|
80
|
+
return None
|
|
81
|
+
if href.startswith("/"):
|
|
82
|
+
return Path(href.lstrip("/"))
|
|
83
|
+
return (public_file.parent / href).resolve().relative_to(ROOT.resolve())
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def scan_file(public_file: Path, contracts: dict[Path, str | None]) -> list[Violation]:
|
|
87
|
+
abs_path = ROOT / public_file
|
|
88
|
+
if not abs_path.exists():
|
|
89
|
+
return []
|
|
90
|
+
violations: list[Violation] = []
|
|
91
|
+
for lineno, line in enumerate(abs_path.read_text(encoding="utf-8").splitlines(), 1):
|
|
92
|
+
for m in LINK_RE.finditer(line):
|
|
93
|
+
href = m.group("href")
|
|
94
|
+
text = m.group("text")
|
|
95
|
+
try:
|
|
96
|
+
target = resolve(public_file, href)
|
|
97
|
+
except ValueError:
|
|
98
|
+
continue
|
|
99
|
+
if target is None:
|
|
100
|
+
continue
|
|
101
|
+
if target.parts[:2] == ("agents", "contexts") and target.suffix == ".md":
|
|
102
|
+
violations.append(Violation(str(public_file), lineno, href,
|
|
103
|
+
"public surface MUST NOT link into agents/contexts/ — move target to docs/contracts/",
|
|
104
|
+
"error"))
|
|
105
|
+
continue
|
|
106
|
+
if target.parts[:2] != ("docs", "contracts") or target.suffix != ".md":
|
|
107
|
+
continue
|
|
108
|
+
if target == STABILITY_FILE:
|
|
109
|
+
continue
|
|
110
|
+
if target not in contracts:
|
|
111
|
+
violations.append(Violation(str(public_file), lineno, href,
|
|
112
|
+
f"target not found: {target}", "error"))
|
|
113
|
+
continue
|
|
114
|
+
level = contracts[target]
|
|
115
|
+
if level is None:
|
|
116
|
+
violations.append(Violation(str(public_file), lineno, href,
|
|
117
|
+
f"target missing 'stability:' frontmatter: {target}", "error"))
|
|
118
|
+
continue
|
|
119
|
+
if level == "experimental":
|
|
120
|
+
violations.append(Violation(str(public_file), lineno, href,
|
|
121
|
+
f"public surface MUST NOT link to experimental contract: {target}",
|
|
122
|
+
"error"))
|
|
123
|
+
continue
|
|
124
|
+
if level == "beta":
|
|
125
|
+
window = line.lower()
|
|
126
|
+
if "(beta)" not in window and "[beta]" not in window:
|
|
127
|
+
violations.append(Violation(str(public_file), lineno, href,
|
|
128
|
+
f"link to beta contract '{target}' lacks visible (beta) marker",
|
|
129
|
+
"warning"))
|
|
130
|
+
return violations
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def main() -> int:
|
|
134
|
+
ap = argparse.ArgumentParser()
|
|
135
|
+
ap.add_argument("--list", action="store_true", help="list contracts + stability levels")
|
|
136
|
+
ap.add_argument("--json", action="store_true", help="machine-readable output")
|
|
137
|
+
ap.add_argument("--strict", action="store_true",
|
|
138
|
+
help="fail on warnings as well as errors (default: errors only)")
|
|
139
|
+
args = ap.parse_args()
|
|
140
|
+
|
|
141
|
+
contracts = collect_contracts()
|
|
142
|
+
if args.list:
|
|
143
|
+
for p, lvl in contracts.items():
|
|
144
|
+
print(f" {lvl or '(no frontmatter)':14} {p}")
|
|
145
|
+
return 0
|
|
146
|
+
|
|
147
|
+
missing_fm = [p for p, lvl in contracts.items() if lvl is None and p != STABILITY_FILE]
|
|
148
|
+
violations: list[Violation] = []
|
|
149
|
+
for p in missing_fm:
|
|
150
|
+
violations.append(Violation(str(p), 0, "(self)",
|
|
151
|
+
"missing 'stability:' frontmatter required by docs/contracts/STABILITY.md",
|
|
152
|
+
"error"))
|
|
153
|
+
for f in PUBLIC_FILES:
|
|
154
|
+
violations.extend(scan_file(f, contracts))
|
|
155
|
+
|
|
156
|
+
if args.json:
|
|
157
|
+
print(json.dumps([asdict(v) for v in violations], indent=2))
|
|
158
|
+
else:
|
|
159
|
+
errors = [v for v in violations if v.severity == "error"]
|
|
160
|
+
warnings = [v for v in violations if v.severity == "warning"]
|
|
161
|
+
for v in violations:
|
|
162
|
+
icon = "❌" if v.severity == "error" else "⚠️ "
|
|
163
|
+
loc = f"{v.file}:{v.line}" if v.line else v.file
|
|
164
|
+
print(f"{icon} {loc} {v.href}\n → {v.reason}")
|
|
165
|
+
if not violations:
|
|
166
|
+
print(f"✅ public-link check clean — {len(contracts)} contracts scanned, "
|
|
167
|
+
f"{len(PUBLIC_FILES)} public files clean")
|
|
168
|
+
else:
|
|
169
|
+
print(f"\nsummary: {len(errors)} error(s), {len(warnings)} warning(s)")
|
|
170
|
+
|
|
171
|
+
has_errors = any(v.severity == "error" for v in violations)
|
|
172
|
+
has_warnings = any(v.severity == "warning" for v in violations)
|
|
173
|
+
if has_errors:
|
|
174
|
+
return 1
|
|
175
|
+
if has_warnings and args.strict:
|
|
176
|
+
return 1
|
|
177
|
+
return 0
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
if __name__ == "__main__":
|
|
181
|
+
try:
|
|
182
|
+
sys.exit(main())
|
|
183
|
+
except Exception as e:
|
|
184
|
+
print(f"❌ internal error: {e}", file=sys.stderr)
|
|
185
|
+
sys.exit(3)
|
|
@@ -32,7 +32,10 @@ class BrokenRef:
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
SCAN_DIRS = [".agent-src", "agents"]
|
|
35
|
-
SKIP_DIRS = [
|
|
35
|
+
SKIP_DIRS = [
|
|
36
|
+
"agents/roadmaps/archive", # archived roadmaps have historical refs
|
|
37
|
+
"agents/council-sessions", # per-user audit trail (gitignored), captured provider output
|
|
38
|
+
]
|
|
36
39
|
ROOT = Path(".")
|
|
37
40
|
|
|
38
41
|
# YAML memory files (engineering-memory layer) live under `agents/memory/`.
|
|
@@ -78,6 +81,7 @@ EXAMPLE_PATH_PATTERNS = [
|
|
|
78
81
|
re.compile(r"agents/overrides/"), # override examples
|
|
79
82
|
re.compile(r"commands/old-cmd"), # example placeholder
|
|
80
83
|
re.compile(r"agents/README"), # README reference (may not exist in package)
|
|
84
|
+
re.compile(r"agents/index[\w.-]*\.md"), # planned auto-generated artefact index (F5)
|
|
81
85
|
re.compile(r"agents/docs/"), # project-specific docs (not in package)
|
|
82
86
|
re.compile(r"agents/contexts/"), # project-specific contexts (not in package)
|
|
83
87
|
re.compile(r"agents/gates"), # project-specific policy docs
|