@event4u/agent-config 1.17.0 → 1.19.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/council/default.md +74 -76
- package/.agent-src/commands/feature/roadmap.md +22 -0
- package/.agent-src/commands/roadmap/create.md +38 -6
- package/.agent-src/commands/roadmap/execute.md +36 -9
- package/.agent-src/rules/agent-authority.md +1 -0
- package/.agent-src/rules/agent-docs.md +1 -0
- package/.agent-src/rules/analysis-skill-routing.md +1 -0
- package/.agent-src/rules/architecture.md +1 -0
- package/.agent-src/rules/artifact-drafting-protocol.md +1 -0
- package/.agent-src/rules/artifact-engagement-recording.md +1 -0
- package/.agent-src/rules/ask-when-uncertain.md +1 -0
- package/.agent-src/rules/augment-portability.md +1 -0
- package/.agent-src/rules/augment-source-of-truth.md +1 -0
- package/.agent-src/rules/autonomous-execution.md +1 -0
- package/.agent-src/rules/capture-learnings.md +1 -0
- package/.agent-src/rules/chat-history-cadence.md +34 -0
- package/.agent-src/rules/chat-history-ownership.md +1 -0
- package/.agent-src/rules/chat-history-visibility.md +1 -0
- package/.agent-src/rules/cli-output-handling.md +2 -2
- package/.agent-src/rules/command-suggestion-policy.md +1 -0
- package/.agent-src/rules/commit-conventions.md +1 -0
- package/.agent-src/rules/commit-policy.md +1 -0
- package/.agent-src/rules/context-hygiene.md +28 -0
- package/.agent-src/rules/direct-answers.md +18 -26
- package/.agent-src/rules/docker-commands.md +1 -0
- package/.agent-src/rules/docs-sync.md +1 -0
- package/.agent-src/rules/downstream-changes.md +1 -0
- package/.agent-src/rules/e2e-testing.md +1 -0
- package/.agent-src/rules/guidelines.md +1 -0
- package/.agent-src/rules/improve-before-implement.md +1 -0
- package/.agent-src/rules/language-and-tone.md +1 -0
- package/.agent-src/rules/laravel-translations.md +1 -0
- package/.agent-src/rules/markdown-safe-codeblocks.md +1 -0
- package/.agent-src/rules/minimal-safe-diff.md +1 -0
- package/.agent-src/rules/missing-tool-handling.md +1 -0
- package/.agent-src/rules/model-recommendation.md +1 -0
- package/.agent-src/rules/no-cheap-questions.md +15 -21
- package/.agent-src/rules/no-roadmap-references.md +1 -0
- package/.agent-src/rules/non-destructive-by-default.md +1 -0
- package/.agent-src/rules/onboarding-gate.md +33 -0
- package/.agent-src/rules/package-ci-checks.md +1 -0
- package/.agent-src/rules/php-coding.md +1 -0
- package/.agent-src/rules/preservation-guard.md +1 -0
- package/.agent-src/rules/review-routing-awareness.md +1 -0
- package/.agent-src/rules/reviewer-awareness.md +1 -0
- package/.agent-src/rules/roadmap-progress-sync.md +49 -0
- package/.agent-src/rules/role-mode-adherence.md +2 -2
- package/.agent-src/rules/rule-type-governance.md +29 -0
- package/.agent-src/rules/runtime-safety.md +1 -0
- package/.agent-src/rules/scope-control.md +1 -0
- package/.agent-src/rules/security-sensitive-stop.md +1 -0
- package/.agent-src/rules/size-enforcement.md +1 -0
- package/.agent-src/rules/skill-improvement-trigger.md +1 -0
- package/.agent-src/rules/skill-quality.md +1 -0
- package/.agent-src/rules/slash-command-routing-policy.md +39 -0
- package/.agent-src/rules/think-before-action.md +1 -0
- package/.agent-src/rules/token-efficiency.md +1 -0
- package/.agent-src/rules/tool-safety.md +1 -0
- package/.agent-src/rules/ui-audit-gate.md +1 -0
- package/.agent-src/rules/upstream-proposal.md +1 -0
- package/.agent-src/rules/user-interaction.md +1 -0
- package/.agent-src/rules/verify-before-complete.md +1 -0
- package/.agent-src/skills/roadmap-management/SKILL.md +29 -4
- package/.agent-src/skills/verify-completion-evidence/SKILL.md +8 -1
- package/.agent-src/templates/agent-settings.md +16 -0
- package/.agent-src/templates/roadmaps.md +12 -3
- package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +9 -0
- package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +4 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +4 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.py +163 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +111 -0
- package/.agent-src/templates/scripts/work_engine/hooks/settings.py +36 -0
- package/.agent-src/templates/scripts/work_engine/scoring/decision_trace.py +141 -0
- package/.agent-src/templates/scripts/work_engine/scoring/memory_visibility.py +125 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +97 -0
- package/README.md +20 -20
- package/config/agent-settings.template.yml +23 -0
- package/docs/architecture.md +1 -1
- package/docs/catalog.md +5 -2
- package/docs/contracts/adr-settings-sync-engine.md +127 -0
- package/docs/contracts/decision-trace-v1.md +146 -0
- package/docs/contracts/file-ownership-matrix.json +7 -0
- package/docs/contracts/hook-architecture-v1.md +213 -0
- package/docs/contracts/load-context-budget-model.md +80 -0
- package/docs/contracts/load-context-schema.md +20 -0
- package/docs/contracts/memory-visibility-v1.md +138 -0
- package/docs/contracts/one-off-script-lifecycle.md +109 -0
- package/docs/contracts/roadmap-complexity-standard.md +137 -0
- package/docs/contracts/rule-interactions.yml +22 -0
- package/docs/customization.md +1 -0
- package/docs/development.md +4 -1
- package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +134 -0
- package/docs/guidelines/agent-infra/direct-answers-demos.md +145 -0
- package/docs/guidelines/agent-infra/layered-settings.md +32 -13
- package/docs/guidelines/agent-infra/verify-before-complete-demos.md +128 -0
- package/package.json +1 -1
- package/scripts/agent-config +64 -0
- package/scripts/ai_council/bundler.py +3 -3
- package/scripts/ai_council/clients.py +24 -8
- package/scripts/ai_council/one_off_archive/2026-05/README.md +67 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_budget_v2_audit.py +206 -0
- package/scripts/ai_council/{_one_off_roundtrip.py → one_off_archive/2026-05/_one_off_roundtrip.py} +13 -8
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_tier_retrofit.py +180 -0
- package/scripts/ai_council/session.py +92 -0
- package/scripts/build_rule_trigger_matrix.py +360 -0
- package/scripts/capture_showcase_session.py +361 -0
- package/scripts/chat_history.py +11 -1
- package/scripts/check_always_budget.py +46 -2
- package/scripts/check_one_off_location.py +81 -0
- package/scripts/check_references.py +6 -0
- package/scripts/compress.py +5 -2
- package/scripts/context_hygiene_hook.py +181 -0
- package/scripts/council_cli.py +357 -0
- package/scripts/hook_manifest.yaml +184 -0
- package/scripts/hooks/__init__.py +1 -0
- package/scripts/hooks/augment-context-hygiene.sh +55 -0
- package/scripts/hooks/augment-dispatcher.sh +72 -0
- package/scripts/hooks/augment-onboarding-gate.sh +55 -0
- package/scripts/hooks/cline-dispatcher.sh +86 -0
- package/scripts/hooks/cursor-dispatcher.sh +76 -0
- package/scripts/hooks/dispatch_hook.py +348 -0
- package/scripts/hooks/envelope.py +98 -0
- package/scripts/hooks/gemini-dispatcher.sh +117 -0
- package/scripts/hooks/state_io.py +122 -0
- package/scripts/hooks/windsurf-dispatcher.sh +123 -0
- package/scripts/hooks_status.py +146 -0
- package/scripts/install.py +728 -51
- package/scripts/install.sh +1 -1
- package/scripts/lint_examples.py +98 -0
- package/scripts/lint_hook_manifest.py +216 -0
- package/scripts/lint_one_off_age.py +184 -0
- package/scripts/lint_roadmap_complexity.py +127 -0
- package/scripts/lint_rule_tiers.py +78 -0
- package/scripts/lint_showcase_sessions.py +148 -0
- package/scripts/minimal_safe_diff_hook.py +245 -0
- package/scripts/onboarding_gate_hook.py +142 -0
- package/scripts/readme_linter.py +12 -3
- package/scripts/roadmap_progress_hook.py +5 -0
- package/scripts/schemas/rule.schema.json +5 -0
- package/scripts/sync_agent_settings.py +32 -129
- package/scripts/sync_yaml_rt.py +734 -0
- package/scripts/verify_before_complete_hook.py +216 -0
- /package/scripts/ai_council/{_one_off_2a4_acceptance.py → one_off_archive/2026-05/_one_off_2a4_acceptance.py} +0 -0
- /package/scripts/ai_council/{_one_off_context_layer_v1_estimate.py → one_off_archive/2026-05/_one_off_context_layer_v1_estimate.py} +0 -0
- /package/scripts/ai_council/{_one_off_context_layer_v1_review.py → one_off_archive/2026-05/_one_off_context_layer_v1_review.py} +0 -0
- /package/scripts/ai_council/{_one_off_followups_review.py → one_off_archive/2026-05/_one_off_followups_review.py} +0 -0
- /package/scripts/ai_council/{_one_off_nondestructive_inline_audit.py → one_off_archive/2026-05/_one_off_nondestructive_inline_audit.py} +0 -0
- /package/scripts/{_one_off_phase4_dispatch_latency.py → ai_council/one_off_archive/2026-05/_one_off_phase4_dispatch_latency.py} +0 -0
- /package/scripts/{_one_off_phase6_trigger_jaccard.py → ai_council/one_off_archive/2026-05/_one_off_phase6_trigger_jaccard.py} +0 -0
- /package/scripts/ai_council/{_one_off_phase_2a_budget_rebalance.py → one_off_archive/2026-05/_one_off_phase_2a_budget_rebalance.py} +0 -0
- /package/scripts/ai_council/{_one_off_phase_2a_post_revert.py → one_off_archive/2026-05/_one_off_phase_2a_post_revert.py} +0 -0
- /package/scripts/ai_council/{_one_off_rebalancing_audit.py → one_off_archive/2026-05/_one_off_rebalancing_audit.py} +0 -0
- /package/scripts/ai_council/{_one_off_rule_hardening_v1.py → one_off_archive/2026-05/_one_off_rule_hardening_v1.py} +0 -0
- /package/scripts/ai_council/{_one_off_structural_open_questions.py → one_off_archive/2026-05/_one_off_structural_open_questions.py} +0 -0
- /package/scripts/ai_council/{_one_off_structural_optimization.py → one_off_archive/2026-05/_one_off_structural_optimization.py} +0 -0
- /package/scripts/ai_council/{_one_off_structural_v3_gaps.py → one_off_archive/2026-05/_one_off_structural_v3_gaps.py} +0 -0
- /package/scripts/ai_council/{_one_off_structural_v3_review.py → one_off_archive/2026-05/_one_off_structural_v3_review.py} +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Windsurf (Cascade) universal hook trampoline (Phase 7.7,
|
|
3
|
+
# hook-architecture-v1.md).
|
|
4
|
+
#
|
|
5
|
+
# Routes Windsurf hook events — fired from either the project-scope
|
|
6
|
+
# `.windsurf/hooks.json` or the user-scope `~/.codeium/windsurf/hooks.json`
|
|
7
|
+
# — into the active workspace's `./agent-config dispatch:hook`.
|
|
8
|
+
#
|
|
9
|
+
# Windsurf event payload (per docs.windsurf.com/windsurf/cascade/hooks):
|
|
10
|
+
# { "agent_action_name": "<event>",
|
|
11
|
+
# "tool_info": { "cwd": "...", "file_path": "...", ... } }
|
|
12
|
+
#
|
|
13
|
+
# Workspace resolution — Windsurf does NOT pass a workspace_roots array
|
|
14
|
+
# the way Cursor/Cline do. Instead:
|
|
15
|
+
# 1. Project-scope hook → cwd is the workspace root (Cascade convention).
|
|
16
|
+
# `$PWD` containing `./agent-config` is the happy path.
|
|
17
|
+
# 2. User-scope hook → cwd may be the workspace, but for some events
|
|
18
|
+
# Windsurf executes from `$HOME` or a tmp dir. Fall back to:
|
|
19
|
+
# - tool_info.cwd from the JSON payload
|
|
20
|
+
# - tool_info.file_path → walk up to nearest .agent-settings.yml
|
|
21
|
+
# - $ROOT_WORKSPACE_PATH (only set on post_setup_worktree)
|
|
22
|
+
# 3. Bail silently when no resolution succeeds — concerns are
|
|
23
|
+
# observe-only at this layer; chat-history / roadmap-progress /
|
|
24
|
+
# context-hygiene never block, and onboarding-gate writes state,
|
|
25
|
+
# not exit code.
|
|
26
|
+
#
|
|
27
|
+
# Output — none. Windsurf does not consume stdout from hooks (post hooks
|
|
28
|
+
# are async, pre hooks block via exit code 2). We always exit 0 since
|
|
29
|
+
# none of our concerns block.
|
|
30
|
+
|
|
31
|
+
set -u
|
|
32
|
+
|
|
33
|
+
# Args from the platform's hooks.json command string:
|
|
34
|
+
# $1 = agent-config event name (session_start, stop, user_prompt_submit)
|
|
35
|
+
# $2 = Windsurf-native event name (pre_user_prompt, post_cascade_response, …)
|
|
36
|
+
EVENT="${1-}"
|
|
37
|
+
NATIVE_EVENT="${2-}"
|
|
38
|
+
|
|
39
|
+
if [ -z "$EVENT" ]; then
|
|
40
|
+
exit 0
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
EVENT_DATA="$(cat)"
|
|
44
|
+
|
|
45
|
+
# 1. $PWD wins when it already looks like an agent-config workspace.
|
|
46
|
+
WORKSPACE=""
|
|
47
|
+
if [ -x "$PWD/agent-config" ] || [ -f "$PWD/.agent-settings.yml" ]; then
|
|
48
|
+
WORKSPACE="$PWD"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# 2. Walk up from $PWD looking for .agent-settings.yml (covers
|
|
52
|
+
# sub-directory invocations).
|
|
53
|
+
if [ -z "$WORKSPACE" ]; then
|
|
54
|
+
candidate="$PWD"
|
|
55
|
+
while [ -n "$candidate" ] && [ "$candidate" != "/" ]; do
|
|
56
|
+
if [ -f "$candidate/.agent-settings.yml" ]; then
|
|
57
|
+
WORKSPACE="$candidate"
|
|
58
|
+
break
|
|
59
|
+
fi
|
|
60
|
+
candidate="$(dirname "$candidate")"
|
|
61
|
+
done
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# 3. Parse JSON tool_info for cwd / file_path.
|
|
65
|
+
if [ -z "$WORKSPACE" ]; then
|
|
66
|
+
if command -v jq >/dev/null 2>&1; then
|
|
67
|
+
EXTRACTED="$(printf '%s' "$EVENT_DATA" \
|
|
68
|
+
| jq -r '.tool_info.cwd // .tool_info.file_path // empty' 2>/dev/null)"
|
|
69
|
+
elif command -v python3 >/dev/null 2>&1; then
|
|
70
|
+
EXTRACTED="$(printf '%s' "$EVENT_DATA" | python3 -c '
|
|
71
|
+
import json, sys
|
|
72
|
+
try:
|
|
73
|
+
data = json.load(sys.stdin)
|
|
74
|
+
except Exception:
|
|
75
|
+
sys.exit(0)
|
|
76
|
+
info = data.get("tool_info") or {}
|
|
77
|
+
print(info.get("cwd") or info.get("file_path") or "")
|
|
78
|
+
' 2>/dev/null)"
|
|
79
|
+
else
|
|
80
|
+
EXTRACTED=""
|
|
81
|
+
fi
|
|
82
|
+
EXTRACTED="${EXTRACTED%$'\r'}"
|
|
83
|
+
if [ -n "$EXTRACTED" ]; then
|
|
84
|
+
# Walk up looking for .agent-settings.yml.
|
|
85
|
+
candidate="$EXTRACTED"
|
|
86
|
+
if [ -f "$candidate" ]; then
|
|
87
|
+
candidate="$(dirname "$candidate")"
|
|
88
|
+
fi
|
|
89
|
+
while [ -n "$candidate" ] && [ "$candidate" != "/" ]; do
|
|
90
|
+
if [ -f "$candidate/.agent-settings.yml" ]; then
|
|
91
|
+
WORKSPACE="$candidate"
|
|
92
|
+
break
|
|
93
|
+
fi
|
|
94
|
+
candidate="$(dirname "$candidate")"
|
|
95
|
+
done
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# 4. $ROOT_WORKSPACE_PATH is set only on post_setup_worktree.
|
|
100
|
+
if [ -z "$WORKSPACE" ] && [ -n "${ROOT_WORKSPACE_PATH-}" ]; then
|
|
101
|
+
if [ -f "$ROOT_WORKSPACE_PATH/.agent-settings.yml" ]; then
|
|
102
|
+
WORKSPACE="$ROOT_WORKSPACE_PATH"
|
|
103
|
+
fi
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
if [ -z "$WORKSPACE" ] || [ ! -d "$WORKSPACE" ]; then
|
|
107
|
+
exit 0
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
cd "$WORKSPACE" 2>/dev/null || exit 0
|
|
111
|
+
|
|
112
|
+
if [ ! -x ./agent-config ]; then
|
|
113
|
+
exit 0
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
printf '%s' "$EVENT_DATA" \
|
|
117
|
+
| ./agent-config dispatch:hook \
|
|
118
|
+
--platform windsurf \
|
|
119
|
+
--event "$EVENT" \
|
|
120
|
+
--native-event "$NATIVE_EVENT" \
|
|
121
|
+
>/dev/null 2>&1 || true
|
|
122
|
+
|
|
123
|
+
exit 0
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Print the runtime hook matrix per `docs/contracts/hook-architecture-v1.md`.
|
|
3
|
+
|
|
4
|
+
For each platform in `scripts/hook_manifest.yaml`, prints whether the
|
|
5
|
+
project-scope bridge files exist on disk, which (event → concerns)
|
|
6
|
+
bindings the manifest declares, and a one-line install hint when the
|
|
7
|
+
bridge is missing. Copilot has no native hook surface — its row carries
|
|
8
|
+
the `degraded: rule-only fallback` marker per Phase 7.12 / Round 2.
|
|
9
|
+
|
|
10
|
+
This is a **read-only** report. It never installs, modifies, or fires
|
|
11
|
+
anything; that is the contract callers depend on (`task hooks-status`,
|
|
12
|
+
post-install smoke, CI).
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import json
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
22
|
+
sys.path.insert(0, str(REPO_ROOT / "scripts" / "hooks"))
|
|
23
|
+
|
|
24
|
+
import dispatch_hook # noqa: E402 — reuse the manifest loader
|
|
25
|
+
|
|
26
|
+
# (label, project-relative bridge path, install hint).
|
|
27
|
+
# Path may be a directory (cline) — existence => any file inside.
|
|
28
|
+
PLATFORM_BRIDGES: dict[str, tuple[str, str]] = {
|
|
29
|
+
"augment": (".augment/settings.json", "scripts/install.py"),
|
|
30
|
+
"claude": (".claude/settings.json", "scripts/install.py"),
|
|
31
|
+
"cursor": (".cursor/hooks.json", "scripts/install.py"),
|
|
32
|
+
"cline": (".clinerules/hooks", "scripts/install.py"),
|
|
33
|
+
"windsurf": (".windsurf/hooks.json", "scripts/install.py"),
|
|
34
|
+
"gemini": (".gemini/settings.json", "scripts/install.py"),
|
|
35
|
+
"copilot": ("", "rule-only fallback (no hook surface)"),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _bridge_status(project_root: Path, rel_path: str) -> str:
|
|
40
|
+
if not rel_path:
|
|
41
|
+
return "n/a"
|
|
42
|
+
target = project_root / rel_path
|
|
43
|
+
if target.is_dir():
|
|
44
|
+
return "installed" if any(target.iterdir()) else "empty"
|
|
45
|
+
return "installed" if target.is_file() else "missing"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def collect(project_root: Path, manifest: dict) -> dict:
|
|
49
|
+
"""Build the runtime matrix as a plain dict — JSON-serialisable."""
|
|
50
|
+
platforms = manifest.get("platforms") or {}
|
|
51
|
+
rows: list[dict] = []
|
|
52
|
+
for platform in PLATFORM_BRIDGES:
|
|
53
|
+
rel, hint = PLATFORM_BRIDGES[platform]
|
|
54
|
+
block = platforms.get(platform) or {}
|
|
55
|
+
fallback_only = bool(block.get("fallback_only"))
|
|
56
|
+
bindings = (
|
|
57
|
+
{} if fallback_only
|
|
58
|
+
else {ev: list(c) for ev, c in block.items()
|
|
59
|
+
if isinstance(c, list)}
|
|
60
|
+
)
|
|
61
|
+
status = "degraded" if fallback_only else _bridge_status(project_root, rel)
|
|
62
|
+
rows.append({
|
|
63
|
+
"platform": platform,
|
|
64
|
+
"status": status,
|
|
65
|
+
"bridge_path": rel or None,
|
|
66
|
+
"fallback_only": fallback_only,
|
|
67
|
+
"bindings": bindings,
|
|
68
|
+
"hint": hint if status in {"missing", "empty", "degraded"} else None,
|
|
69
|
+
})
|
|
70
|
+
return {"schema_version": 1, "platforms": rows}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _render_table(matrix: dict) -> str:
|
|
74
|
+
lines: list[str] = []
|
|
75
|
+
lines.append("agent-config hook matrix")
|
|
76
|
+
lines.append("=" * 60)
|
|
77
|
+
for row in matrix["platforms"]:
|
|
78
|
+
marker = {
|
|
79
|
+
"installed": "✅ ",
|
|
80
|
+
"missing": "❌ ",
|
|
81
|
+
"empty": "⚠️ ",
|
|
82
|
+
"degraded": "⚠️ ",
|
|
83
|
+
"n/a": "· ",
|
|
84
|
+
}.get(row["status"], "? ")
|
|
85
|
+
head = f"{marker}{row['platform']:<9} {row['status']}"
|
|
86
|
+
if row["bridge_path"]:
|
|
87
|
+
head += f" ({row['bridge_path']})"
|
|
88
|
+
lines.append(head)
|
|
89
|
+
if row["fallback_only"]:
|
|
90
|
+
lines.append(" degraded: rule-only fallback "
|
|
91
|
+
"— hooks are not auto-firing on this platform.")
|
|
92
|
+
continue
|
|
93
|
+
if not row["bindings"]:
|
|
94
|
+
lines.append(" (no bindings declared in manifest)")
|
|
95
|
+
continue
|
|
96
|
+
for event in sorted(row["bindings"]):
|
|
97
|
+
concerns = ", ".join(row["bindings"][event]) or "—"
|
|
98
|
+
lines.append(f" {event:<22} → {concerns}")
|
|
99
|
+
if row["hint"]:
|
|
100
|
+
lines.append(f" hint: run {row['hint']}")
|
|
101
|
+
lines.append("")
|
|
102
|
+
lines.append("Source of truth: scripts/hook_manifest.yaml")
|
|
103
|
+
lines.append("Contract: docs/contracts/hook-architecture-v1.md")
|
|
104
|
+
return "\n".join(lines)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _final_exit_code(matrix: dict, strict: bool) -> int:
|
|
108
|
+
if not strict:
|
|
109
|
+
return 0
|
|
110
|
+
# Strict mode: any platform with declared bindings whose bridge is
|
|
111
|
+
# missing is a CI failure. `degraded`/`n/a` never fail (Copilot is
|
|
112
|
+
# an explicit no-hook platform; n/a means no bridge expected).
|
|
113
|
+
for row in matrix["platforms"]:
|
|
114
|
+
if row["status"] == "missing" and row["bindings"]:
|
|
115
|
+
return 1
|
|
116
|
+
return 0
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def main(argv: list[str] | None = None) -> int:
|
|
120
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
121
|
+
parser.add_argument("--format", choices=["table", "json"], default="table")
|
|
122
|
+
parser.add_argument("--project-root", default=".",
|
|
123
|
+
help="Project root to inspect (default: cwd)")
|
|
124
|
+
parser.add_argument("--manifest", default=str(dispatch_hook.MANIFEST_PATH))
|
|
125
|
+
parser.add_argument("--strict", action="store_true",
|
|
126
|
+
help="Exit non-zero if any platform with bindings is "
|
|
127
|
+
"missing its bridge (CI-friendly).")
|
|
128
|
+
args = parser.parse_args(argv)
|
|
129
|
+
|
|
130
|
+
manifest_path = Path(args.manifest)
|
|
131
|
+
if not manifest_path.exists():
|
|
132
|
+
sys.stderr.write(f"hooks_status: manifest missing at {manifest_path}\n")
|
|
133
|
+
return 2
|
|
134
|
+
manifest = dispatch_hook._load_yaml(manifest_path)
|
|
135
|
+
project_root = Path(args.project_root).resolve()
|
|
136
|
+
matrix = collect(project_root, manifest)
|
|
137
|
+
|
|
138
|
+
if args.format == "json":
|
|
139
|
+
print(json.dumps(matrix, indent=2, sort_keys=True))
|
|
140
|
+
else:
|
|
141
|
+
print(_render_table(matrix))
|
|
142
|
+
return _final_exit_code(matrix, args.strict)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if __name__ == "__main__":
|
|
146
|
+
raise SystemExit(main())
|