@event4u/agent-config 2.7.0 → 2.9.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/personas/cmo.md +122 -0
- package/.agent-src/personas/customer-success-lead.md +126 -0
- package/.agent-src/personas/engineering-manager.md +133 -0
- package/.agent-src/personas/finance-partner.md +129 -0
- package/.agent-src/personas/growth-pm.md +134 -0
- package/.agent-src/personas/people-strategist.md +126 -0
- package/.agent-src/personas/revops.md +125 -0
- package/.agent-src/personas/strategist.md +129 -0
- package/.agent-src/skills/activation-design/SKILL.md +160 -0
- package/.agent-src/skills/build-buy-partner/SKILL.md +145 -0
- package/.agent-src/skills/churn-prevention/SKILL.md +156 -0
- package/.agent-src/skills/comp-banding/SKILL.md +160 -0
- package/.agent-src/skills/competitive-moat-analysis/SKILL.md +152 -0
- package/.agent-src/skills/content-funnel-design/SKILL.md +170 -0
- package/.agent-src/skills/contracts-cognition/SKILL.md +147 -0
- package/.agent-src/skills/data-handling-judgment/SKILL.md +155 -0
- package/.agent-src/skills/deal-qualification-meddic/SKILL.md +165 -0
- package/.agent-src/skills/editorial-calendar/SKILL.md +161 -0
- package/.agent-src/skills/expansion-playbook/SKILL.md +171 -0
- package/.agent-src/skills/forecast-accuracy/SKILL.md +157 -0
- package/.agent-src/skills/forecasting/SKILL.md +164 -0
- package/.agent-src/skills/fundraising-narrative/SKILL.md +189 -0
- package/.agent-src/skills/funnel-analysis/SKILL.md +26 -2
- package/.agent-src/skills/gtm-launch/SKILL.md +165 -0
- package/.agent-src/skills/hiring-loop-design/SKILL.md +167 -0
- package/.agent-src/skills/market-entry-analysis/SKILL.md +144 -0
- package/.agent-src/skills/messaging-architecture/SKILL.md +184 -0
- package/.agent-src/skills/onboarding-design/SKILL.md +158 -0
- package/.agent-src/skills/onboarding-program/SKILL.md +157 -0
- package/.agent-src/skills/one-on-one-cadence/SKILL.md +161 -0
- package/.agent-src/skills/org-design/SKILL.md +158 -0
- package/.agent-src/skills/perf-feedback-craft/SKILL.md +157 -0
- package/.agent-src/skills/pipeline-strategy/SKILL.md +159 -0
- package/.agent-src/skills/positioning-strategy/SKILL.md +177 -0
- package/.agent-src/skills/privacy-review/SKILL.md +160 -0
- package/.agent-src/skills/retention-loops/SKILL.md +161 -0
- package/.agent-src/skills/runway-cognition/SKILL.md +136 -0
- package/.agent-src/skills/scenario-modeling/SKILL.md +139 -0
- package/.agent-src/skills/subagent-orchestration/SKILL.md +1 -1
- package/.agent-src/skills/throughput-vs-morale-tradeoff/SKILL.md +165 -0
- package/.agent-src/skills/unit-economics-modeling/SKILL.md +54 -7
- package/.agent-src/skills/vision-articulation/SKILL.md +146 -0
- package/.agent-src/skills/voice-and-tone-design/SKILL.md +163 -0
- package/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/.agent-src/templates/scripts/telemetry/settings.py +65 -0
- package/.agent-src/templates/scripts/tier_usage_report.py +183 -0
- package/.claude-plugin/marketplace.json +34 -2
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +135 -153
- package/README.md +3 -3
- package/docs/architecture.md +37 -11
- package/docs/archive/CHANGELOG-pre-2.7.0.md +185 -0
- package/docs/catalog.md +38 -4
- package/docs/contracts/adr-forecast-construction-shape.md +89 -0
- package/docs/contracts/adr-gtm-context-spine.md +115 -0
- package/docs/contracts/adr-wing4-context-spine.md +125 -0
- package/docs/contracts/command-clusters.md +41 -0
- package/docs/contracts/command-surface-tiers.md +30 -9
- package/docs/contracts/context-spine.md +58 -12
- package/docs/contracts/cross-wing-handoff.md +3 -3
- package/docs/contracts/mcp-beta-criteria.md +129 -0
- package/docs/contracts/persona-schema.md +20 -3
- package/docs/guidelines/gtm-handoff.md +114 -0
- package/docs/guidelines/wing4-handoff.md +127 -0
- package/docs/mcp-server.md +1 -1
- package/package.json +1 -1
- package/scripts/_cli/cmd_doctor.py +527 -14
- package/scripts/_cli/cmd_validate.py +10 -0
- package/scripts/agent-config +19 -18
- package/scripts/install.py +5 -0
- package/scripts/lint_context_spine_usage.py +5 -1
- package/scripts/mcp_server/__init__.py +1 -0
- package/scripts/mcp_server/server.py +4 -3
- package/scripts/schemas/persona.schema.json +5 -0
- package/scripts/schemas/skill.schema.json +2 -2
- package/scripts/skill_linter.py +284 -6
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: voice-and-tone-design
|
|
3
|
+
description: "Use when shaping brand voice — voice attributes, tone-by-context matrix, consistency review. Triggers on 'define our voice', 'why does our copy sound different on every surface'."
|
|
4
|
+
status: active
|
|
5
|
+
tier: senior
|
|
6
|
+
source: package
|
|
7
|
+
domain: product
|
|
8
|
+
context_spine: [product, customer-segment]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# voice-and-tone-design
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
- A team is producing copy across multiple surfaces and the same segment hears three different brands per quarter — name the voice so the surfaces converge.
|
|
16
|
+
- An audience-by-message matrix exists but each audience cell currently reads in a different tone, breaking the recognition signal across the segment.
|
|
17
|
+
- A new senior persona (CMO, founder, head-of-content) is asking for a voice-and-tone artefact that downstream writers can defend against.
|
|
18
|
+
|
|
19
|
+
Do NOT use to copy-edit individual assets (out of scope — that is a
|
|
20
|
+
writer's job), draft the message stack (route to
|
|
21
|
+
`messaging-architecture`), or pick channel-specific tactics like
|
|
22
|
+
subject lines or ad creative formats (channel-agnostic skill).
|
|
23
|
+
|
|
24
|
+
## Cognition cluster
|
|
25
|
+
|
|
26
|
+
- **Mental model 13 — Occam's Razor.** The simplest voice the
|
|
27
|
+
segment can recognise across surfaces is the voice that survives
|
|
28
|
+
contact with three writers under deadline. A five-attribute voice
|
|
29
|
+
that no one remembers is brand-theatre. See
|
|
30
|
+
[`docs/contracts/mental-models.md`](../../../docs/contracts/mental-models.md) § 13.
|
|
31
|
+
- **Mental model 15 — Signal vs. noise.** Voice is the
|
|
32
|
+
identification signal embedded in copy — without distinctiveness
|
|
33
|
+
it is noise that competes with every peer's noise. Cut to the
|
|
34
|
+
three attributes that *only* the segment hears as us. See
|
|
35
|
+
`mental-models.md` § 15.
|
|
36
|
+
- **Context-spine — product + customer-segment.** Read **product**
|
|
37
|
+
for the proofs the voice must carry (a voice that promises what
|
|
38
|
+
the product cannot back is fiction); read **customer-segment**
|
|
39
|
+
for the listening-register the audience already lives in. See
|
|
40
|
+
[`context-spine`](../../../docs/contracts/context-spine.md).
|
|
41
|
+
|
|
42
|
+
## Procedure
|
|
43
|
+
|
|
44
|
+
### Step 0: Inherit the positioning frame and audience matrix
|
|
45
|
+
|
|
46
|
+
Identify the locked positioning anchors from
|
|
47
|
+
[`positioning-strategy`](../positioning-strategy/SKILL.md) and the audience matrix
|
|
48
|
+
from [`messaging-architecture`](../messaging-architecture/SKILL.md).
|
|
49
|
+
Voice without positioning is style; voice without an audience matrix
|
|
50
|
+
is broadcast.
|
|
51
|
+
|
|
52
|
+
### Step 1: Analyze the inherited voice
|
|
53
|
+
|
|
54
|
+
Read 6–10 recent assets across the largest surface set the team
|
|
55
|
+
ships on. For each, note the *register* (formal · conversational ·
|
|
56
|
+
technical · playful) and one *line that sounds like us* (or *like
|
|
57
|
+
anyone*). The output is an honest audit: what voice is currently
|
|
58
|
+
shipping, not what the team intends to ship.
|
|
59
|
+
|
|
60
|
+
### Step 2: Pick three voice attributes — and three only
|
|
61
|
+
|
|
62
|
+
Three attributes, each phrased as *"\<we are\> X, not Y."* The
|
|
63
|
+
*not Y* is non-trivial: it names the credible adjacent voice the
|
|
64
|
+
team is choosing against, not a strawman. Examples of the shape
|
|
65
|
+
(not vocabulary):
|
|
66
|
+
|
|
67
|
+
- *"Precise, not pedantic."*
|
|
68
|
+
- *"Confident, not boastful."*
|
|
69
|
+
- *"Concrete, not buzzword."*
|
|
70
|
+
|
|
71
|
+
A four-attribute voice will collapse to three under deadline; pick
|
|
72
|
+
the three now.
|
|
73
|
+
|
|
74
|
+
### Step 3: Build the tone-by-context matrix
|
|
75
|
+
|
|
76
|
+
Tone modulates voice; voice is constant. For each context — error
|
|
77
|
+
copy, marketing surface, sales follow-up, onboarding, executive
|
|
78
|
+
summary, support reply — fill: *what the audience is feeling · what
|
|
79
|
+
the surface must do · which voice attribute leads · what tone
|
|
80
|
+
amplifier or muter applies.* The matrix is the tool writers
|
|
81
|
+
actually reach for; the three attributes are the spine they hang
|
|
82
|
+
the tone on.
|
|
83
|
+
|
|
84
|
+
### Step 4: Validate against the recognition signal
|
|
85
|
+
|
|
86
|
+
Validate the voice on three checks:
|
|
87
|
+
|
|
88
|
+
1. **Distinctiveness.** Write the same sentence in three credible
|
|
89
|
+
peer voices. Verify the team's voice is recognisable as distinct
|
|
90
|
+
on first read — not on the second read after explaining the
|
|
91
|
+
attributes.
|
|
92
|
+
2. **Proof-coverage.** Confirm the voice does not promise what the
|
|
93
|
+
product (from the spine) cannot back. *"Calm"* without an
|
|
94
|
+
uptime story is fiction. Verify each attribute against a proof.
|
|
95
|
+
3. **Survives-deadline.** Hand the artefacts to a writer who was
|
|
96
|
+
not in the room. If the writer cannot reproduce the voice on a
|
|
97
|
+
30-minute draft, the voice is documented for the room, not the
|
|
98
|
+
surface.
|
|
99
|
+
|
|
100
|
+
### Step 5: Run the consistency review
|
|
101
|
+
|
|
102
|
+
Re-audit the assets from Step 1 against the locked attributes and
|
|
103
|
+
tone matrix. Mark each line *consistent · slipping · contradiction.*
|
|
104
|
+
Slipping is a writer-coaching problem; contradiction is a re-write.
|
|
105
|
+
The review surfaces the load-bearing surfaces that need a re-pass,
|
|
106
|
+
not a punitive list.
|
|
107
|
+
|
|
108
|
+
### Step 6: Hand back
|
|
109
|
+
|
|
110
|
+
Hand the artefacts to writers across the surfaces, to
|
|
111
|
+
[`editorial-calendar`](../editorial-calendar/SKILL.md) for cadence
|
|
112
|
+
mapping, and to [`release-comms`](../release-comms/SKILL.md) for
|
|
113
|
+
launch-surface voice review.
|
|
114
|
+
|
|
115
|
+
## Related Skills
|
|
116
|
+
|
|
117
|
+
**WHEN to use this**
|
|
118
|
+
|
|
119
|
+
- The unit of work is the *voice* (three attributes + tone-by-context matrix), not a single line of copy.
|
|
120
|
+
- Surfaces are diverging in tone and the segment cannot recognise the brand across them.
|
|
121
|
+
- A team needs an artefact that a writer on deadline can actually use.
|
|
122
|
+
|
|
123
|
+
**WHEN NOT to use this**
|
|
124
|
+
|
|
125
|
+
- Drafting the message stack (primary message + proofs) — route to [`messaging-architecture`](../messaging-architecture/SKILL.md).
|
|
126
|
+
- Cadence and content-debt management — route to [`editorial-calendar`](../editorial-calendar/SKILL.md).
|
|
127
|
+
- Copy-editing individual assets — out of scope; that is a writer's craft.
|
|
128
|
+
- Launch-wave announcement copy — route to [`release-comms`](../release-comms/SKILL.md).
|
|
129
|
+
|
|
130
|
+
## When the agent should load this
|
|
131
|
+
|
|
132
|
+
- "Define our voice — three attributes, no more."
|
|
133
|
+
- "Mach uns einen Tone-by-Context matrix, der Schreibern auf Deadline hilft."
|
|
134
|
+
- "Why does our copy sound different on every surface?"
|
|
135
|
+
- "Run a voice consistency audit on last quarter's assets."
|
|
136
|
+
- "Pick the *not-Y* for each voice attribute."
|
|
137
|
+
|
|
138
|
+
## Output
|
|
139
|
+
|
|
140
|
+
1. **`voice-attributes.md`** — three attributes in the *"X, not Y"* shape, with the proof-from-spine that backs each.
|
|
141
|
+
2. **`tone-by-context.md`** — one row per surface context, with audience feeling, surface job, lead attribute, and tone amplifier / muter.
|
|
142
|
+
3. **`consistency-review.md`** — audit of 6–10 recent assets, each line marked consistent · slipping · contradiction, with re-pass list ranked by surface load.
|
|
143
|
+
|
|
144
|
+
## Gotcha
|
|
145
|
+
|
|
146
|
+
- A voice that pleases the room is usually below the noise floor — agreement is consensus, not distinctiveness.
|
|
147
|
+
- The *not-Y* is the work. *"Confident, not boastful"* is meaningful; *"confident, not weak"* is filler.
|
|
148
|
+
- Tone-by-context matrices over-amplify when the surface is high-stakes — a calm voice in error copy is the discipline, not the failure.
|
|
149
|
+
|
|
150
|
+
## Do NOT
|
|
151
|
+
|
|
152
|
+
- Do NOT exceed three attributes — the fourth collapses on the first deadline.
|
|
153
|
+
- Do NOT prescribe channel-specific tactics or copywriting rules; tactics live with the channel owner.
|
|
154
|
+
- Do NOT promise voice attributes the product cannot back — *"effortless"* without an integration story is fiction the support queue will pay for.
|
|
155
|
+
|
|
156
|
+
## Runnable example
|
|
157
|
+
|
|
158
|
+
Mid-market HR analytics tool, positioning locked (retention beats acquisition), audience matrix has HR director · CFO · IT-security:
|
|
159
|
+
|
|
160
|
+
- Voice attributes — *"Precise, not pedantic."* · *"Confident, not boastful."* · *"Concrete, not buzzword."*
|
|
161
|
+
- Tone-by-context — *Error copy:* audience scared, lead with *precise*, mute *confident*. *Marketing surface:* audience curious, lead with *concrete*, amplify *confident*. *Onboarding:* audience uncertain, lead with *precise*, amplify warmth modifier.
|
|
162
|
+
- Consistency review — 8 of 10 surveyed assets *consistent*; 1 *slipping* (CEO blog leans pedantic); 1 *contradiction* (homepage hero contradicts *not buzzword* with "synergistic"). Re-pass list: homepage hero (load-bearing surface, prioritised).
|
|
163
|
+
- Hand-off → writers across surfaces; `release-comms` voice-reviews the GA-wave announcement.
|
|
@@ -39,7 +39,7 @@ schema_version: 1
|
|
|
39
39
|
# CI guard: a release bump of `package.json` must update this value
|
|
40
40
|
# in lockstep — see scripts/check_template_pin_drift.py (road-to-
|
|
41
41
|
# portable-runtime-and-update-check P3.3).
|
|
42
|
-
agent_config_version: "2.
|
|
42
|
+
agent_config_version: "2.8.0"
|
|
43
43
|
|
|
44
44
|
# --- Project identity ---
|
|
45
45
|
project:
|
|
@@ -18,6 +18,12 @@ DEFAULT_LOG_PATH = Path(".agent-engagement.jsonl")
|
|
|
18
18
|
DEFAULT_GRANULARITY = "task"
|
|
19
19
|
ALLOWED_GRANULARITIES = ("task", "phase-step", "tool-call")
|
|
20
20
|
|
|
21
|
+
#: Defaults for the tier-usage signal (Phase 5 of road-to-surface-discipline).
|
|
22
|
+
#: Separate file, separate opt-in, same default-off posture. Contract:
|
|
23
|
+
#: ``docs/contracts/command-clusters.md#tier-usage-signal-contract``.
|
|
24
|
+
DEFAULT_TIER_USAGE_LOG_PATH = Path(".agent-tier-usage.jsonl")
|
|
25
|
+
DEFAULT_TIER_USAGE_RETIER = {"window_days": 30, "min_invocations": 20, "min_distinct_users": 3}
|
|
26
|
+
|
|
21
27
|
|
|
22
28
|
@dataclass(frozen=True)
|
|
23
29
|
class TelemetrySettings:
|
|
@@ -104,9 +110,68 @@ def read_settings(path: Path) -> TelemetrySettings:
|
|
|
104
110
|
return settings
|
|
105
111
|
|
|
106
112
|
|
|
113
|
+
@dataclass(frozen=True)
|
|
114
|
+
class TierUsageSettings:
|
|
115
|
+
enabled: bool
|
|
116
|
+
log_path: Path
|
|
117
|
+
window_days: int
|
|
118
|
+
min_invocations: int
|
|
119
|
+
min_distinct_users: int
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def read_tier_usage_settings(path: Path) -> TierUsageSettings:
|
|
123
|
+
"""Return parsed tier-usage settings — never raises on missing data.
|
|
124
|
+
|
|
125
|
+
Sibling of :func:`read_settings`; reads the
|
|
126
|
+
``telemetry.tier_usage`` namespace from ``.agent-settings.yml``.
|
|
127
|
+
Default-off, same parse-tolerant shape — a missing file, a missing
|
|
128
|
+
section, or PyYAML being absent all collapse to ``enabled=False``.
|
|
129
|
+
"""
|
|
130
|
+
section: dict[str, Any] = {}
|
|
131
|
+
if path.is_file():
|
|
132
|
+
try:
|
|
133
|
+
import yaml # type: ignore[import-not-found]
|
|
134
|
+
except ImportError:
|
|
135
|
+
yaml = None # type: ignore[assignment]
|
|
136
|
+
if yaml is not None:
|
|
137
|
+
try:
|
|
138
|
+
raw = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
|
|
139
|
+
except Exception:
|
|
140
|
+
raw = {}
|
|
141
|
+
if isinstance(raw, dict):
|
|
142
|
+
tele = raw.get("telemetry")
|
|
143
|
+
if isinstance(tele, dict):
|
|
144
|
+
tu = tele.get("tier_usage")
|
|
145
|
+
if isinstance(tu, dict):
|
|
146
|
+
section = tu
|
|
147
|
+
|
|
148
|
+
output = section.get("output") if isinstance(section.get("output"), dict) else {}
|
|
149
|
+
retier = section.get("retier") if isinstance(section.get("retier"), dict) else {}
|
|
150
|
+
defaults = DEFAULT_TIER_USAGE_RETIER
|
|
151
|
+
|
|
152
|
+
def _coerce_int(value: Any, default: int) -> int:
|
|
153
|
+
if isinstance(value, bool):
|
|
154
|
+
return default
|
|
155
|
+
if isinstance(value, int) and value >= 0:
|
|
156
|
+
return value
|
|
157
|
+
return default
|
|
158
|
+
|
|
159
|
+
return TierUsageSettings(
|
|
160
|
+
enabled=_coerce_bool(section.get("enabled"), default=False),
|
|
161
|
+
log_path=_coerce_path(output.get("path"), DEFAULT_TIER_USAGE_LOG_PATH),
|
|
162
|
+
window_days=_coerce_int(retier.get("window_days"), defaults["window_days"]),
|
|
163
|
+
min_invocations=_coerce_int(retier.get("min_invocations"), defaults["min_invocations"]),
|
|
164
|
+
min_distinct_users=_coerce_int(retier.get("min_distinct_users"), defaults["min_distinct_users"]),
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
107
168
|
__all__ = [
|
|
108
169
|
"DEFAULT_GRANULARITY",
|
|
109
170
|
"DEFAULT_LOG_PATH",
|
|
171
|
+
"DEFAULT_TIER_USAGE_LOG_PATH",
|
|
172
|
+
"DEFAULT_TIER_USAGE_RETIER",
|
|
110
173
|
"TelemetrySettings",
|
|
174
|
+
"TierUsageSettings",
|
|
111
175
|
"read_settings",
|
|
176
|
+
"read_tier_usage_settings",
|
|
112
177
|
]
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Tier-usage report — aggregate the local tier-usage log into a frequency table.
|
|
3
|
+
|
|
4
|
+
Phase 5 Step 3 of road-to-surface-discipline. Reads the JSONL log
|
|
5
|
+
written by the dispatcher (default ``.agent-tier-usage.jsonl``; override
|
|
6
|
+
via ``telemetry.tier_usage.output.path``) and emits a per-command
|
|
7
|
+
frequency table grouped by tier, plus distinct ``user_hash`` counts.
|
|
8
|
+
Run-local-only; no upload, no remote aggregation.
|
|
9
|
+
|
|
10
|
+
Privacy floor mirrors the contract in
|
|
11
|
+
``docs/contracts/command-clusters.md#tier-usage-signal-contract`` and
|
|
12
|
+
the four-layer enforcement model used by artefact-engagement telemetry.
|
|
13
|
+
Records that carry any field outside the contract whitelist are dropped
|
|
14
|
+
at the read gate — the report refuses to render leaked shapes rather
|
|
15
|
+
than re-emit them.
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
python3 tier_usage_report.py # last 30d, table
|
|
19
|
+
python3 tier_usage_report.py --window-days 7 # last 7d
|
|
20
|
+
python3 tier_usage_report.py --window-days 0 # full log
|
|
21
|
+
python3 tier_usage_report.py --json # JSON for tooling
|
|
22
|
+
python3 tier_usage_report.py --log-path X.jsonl # archived snapshot
|
|
23
|
+
|
|
24
|
+
Exit codes:
|
|
25
|
+
0 success or telemetry disabled (single header line)
|
|
26
|
+
1 no records survived the privacy floor on a non-empty file
|
|
27
|
+
2 IO error (permission denied; passed path missing)
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import argparse
|
|
32
|
+
import json
|
|
33
|
+
import sys
|
|
34
|
+
from collections import defaultdict
|
|
35
|
+
from datetime import datetime, timedelta, timezone
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
from typing import Any
|
|
38
|
+
|
|
39
|
+
from telemetry.settings import DEFAULT_TIER_USAGE_LOG_PATH, read_tier_usage_settings
|
|
40
|
+
|
|
41
|
+
#: Contract whitelist (see ``docs/contracts/command-clusters.md``).
|
|
42
|
+
ALLOWED_FIELDS = frozenset({"ts_bucket", "command", "tier", "outcome", "user_hash"})
|
|
43
|
+
ALLOWED_OUTCOMES = frozenset({"success", "error", "blocked"})
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _parse_record(raw: str) -> dict[str, Any] | None:
|
|
47
|
+
"""Return a sanitized record or ``None`` when the line violates the floor."""
|
|
48
|
+
try:
|
|
49
|
+
obj = json.loads(raw)
|
|
50
|
+
except json.JSONDecodeError:
|
|
51
|
+
return None
|
|
52
|
+
if not isinstance(obj, dict):
|
|
53
|
+
return None
|
|
54
|
+
if not set(obj.keys()).issubset(ALLOWED_FIELDS):
|
|
55
|
+
return None
|
|
56
|
+
cmd = obj.get("command")
|
|
57
|
+
if not isinstance(cmd, str) or not cmd or "/" in cmd or "\\" in cmd:
|
|
58
|
+
return None
|
|
59
|
+
if not isinstance(obj.get("tier"), int) or obj["tier"] not in (0, 1, 2, 3):
|
|
60
|
+
return None
|
|
61
|
+
if obj.get("outcome") not in ALLOWED_OUTCOMES:
|
|
62
|
+
return None
|
|
63
|
+
uh = obj.get("user_hash")
|
|
64
|
+
if not isinstance(uh, str) or len(uh) != 16:
|
|
65
|
+
return None
|
|
66
|
+
if not isinstance(obj.get("ts_bucket"), str):
|
|
67
|
+
return None
|
|
68
|
+
return obj
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _within_window(ts_bucket: str, window_days: int | None) -> bool:
|
|
72
|
+
if window_days is None or window_days == 0:
|
|
73
|
+
return True
|
|
74
|
+
try:
|
|
75
|
+
ts = datetime.fromisoformat(ts_bucket.replace("Z", "+00:00"))
|
|
76
|
+
except ValueError:
|
|
77
|
+
return False
|
|
78
|
+
if ts.tzinfo is None:
|
|
79
|
+
ts = ts.replace(tzinfo=timezone.utc)
|
|
80
|
+
return ts >= datetime.now(timezone.utc) - timedelta(days=window_days)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def aggregate(
|
|
84
|
+
log_path: Path, window_days: int,
|
|
85
|
+
) -> tuple[dict[tuple[int, str], dict[str, Any]], int, int]:
|
|
86
|
+
"""Return ``((tier, command) -> stats, total_lines, kept)`` over the window."""
|
|
87
|
+
buckets: dict[tuple[int, str], dict[str, Any]] = defaultdict(
|
|
88
|
+
lambda: {"count": 0, "users": set()},
|
|
89
|
+
)
|
|
90
|
+
total = 0
|
|
91
|
+
kept = 0
|
|
92
|
+
if not log_path.exists():
|
|
93
|
+
return {}, 0, 0
|
|
94
|
+
with log_path.open("r", encoding="utf-8") as fh:
|
|
95
|
+
for line in fh:
|
|
96
|
+
line = line.strip()
|
|
97
|
+
if not line:
|
|
98
|
+
continue
|
|
99
|
+
total += 1
|
|
100
|
+
rec = _parse_record(line)
|
|
101
|
+
if rec is None:
|
|
102
|
+
continue
|
|
103
|
+
if not _within_window(rec["ts_bucket"], window_days):
|
|
104
|
+
continue
|
|
105
|
+
kept += 1
|
|
106
|
+
key = (int(rec["tier"]), rec["command"])
|
|
107
|
+
buckets[key]["count"] += 1
|
|
108
|
+
buckets[key]["users"].add(rec["user_hash"])
|
|
109
|
+
out = {k: {"count": v["count"], "distinct_users": len(v["users"])}
|
|
110
|
+
for k, v in buckets.items()}
|
|
111
|
+
return out, total, kept
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def render(
|
|
115
|
+
table: dict[tuple[int, str], dict[str, Any]],
|
|
116
|
+
window_days: int,
|
|
117
|
+
) -> str:
|
|
118
|
+
suffix = f" (last {window_days}d)" if window_days else " (full log)"
|
|
119
|
+
if not table:
|
|
120
|
+
return f"(no tier-usage records{suffix})\n"
|
|
121
|
+
rows = sorted(table.items(), key=lambda kv: (kv[0][0], -kv[1]["count"], kv[0][1]))
|
|
122
|
+
header = f"{'Tier':<6}{'Command':<32}{'Calls':>8}{'Users':>8}"
|
|
123
|
+
lines = [header, "-" * len(header)]
|
|
124
|
+
for (tier, command), stats in rows:
|
|
125
|
+
lines.append(
|
|
126
|
+
f"{tier:<6}{command:<32}{stats['count']:>8}{stats['distinct_users']:>8}",
|
|
127
|
+
)
|
|
128
|
+
lines.append(f"\n(window:{suffix.strip()})")
|
|
129
|
+
return "\n".join(lines) + "\n"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def main(argv: list[str] | None = None) -> int:
|
|
133
|
+
parser = argparse.ArgumentParser(description="Tier-usage frequency report.")
|
|
134
|
+
parser.add_argument("--window-days", type=int, default=30,
|
|
135
|
+
help="trailing window in days (0 = full log)")
|
|
136
|
+
parser.add_argument("--json", action="store_true",
|
|
137
|
+
help="emit JSON instead of the table")
|
|
138
|
+
parser.add_argument("--log-path", type=Path, default=None,
|
|
139
|
+
help="override settings; read an archived log")
|
|
140
|
+
parser.add_argument("--settings-file", type=Path, default=Path(".agent-settings.yml"))
|
|
141
|
+
args = parser.parse_args(argv)
|
|
142
|
+
|
|
143
|
+
settings = read_tier_usage_settings(args.settings_file)
|
|
144
|
+
log_path = args.log_path or settings.log_path or DEFAULT_TIER_USAGE_LOG_PATH
|
|
145
|
+
|
|
146
|
+
if args.log_path is None and not settings.enabled:
|
|
147
|
+
sys.stdout.write(
|
|
148
|
+
"(tier-usage telemetry disabled; set "
|
|
149
|
+
"`telemetry.tier_usage.enabled: true` in .agent-settings.yml)\n",
|
|
150
|
+
)
|
|
151
|
+
return 0
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
table, total, kept = aggregate(log_path, args.window_days)
|
|
155
|
+
except OSError as exc:
|
|
156
|
+
print(f"❌ {exc}", file=sys.stderr)
|
|
157
|
+
return 2
|
|
158
|
+
|
|
159
|
+
if total > 0 and kept == 0:
|
|
160
|
+
print(f"❌ {total} record(s) read; 0 survived the privacy floor — "
|
|
161
|
+
"report refused", file=sys.stderr)
|
|
162
|
+
return 1
|
|
163
|
+
|
|
164
|
+
if args.json:
|
|
165
|
+
payload = {
|
|
166
|
+
"window_days": args.window_days,
|
|
167
|
+
"log_path": str(log_path),
|
|
168
|
+
"records_total": total,
|
|
169
|
+
"records_kept": kept,
|
|
170
|
+
"rows": [
|
|
171
|
+
{"tier": t, "command": c, "count": v["count"],
|
|
172
|
+
"distinct_users": v["distinct_users"]}
|
|
173
|
+
for (t, c), v in sorted(table.items(), key=lambda kv: (kv[0][0], kv[0][1]))
|
|
174
|
+
],
|
|
175
|
+
}
|
|
176
|
+
sys.stdout.write(json.dumps(payload, indent=2) + "\n")
|
|
177
|
+
else:
|
|
178
|
+
sys.stdout.write(render(table, args.window_days))
|
|
179
|
+
return 0
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if __name__ == "__main__":
|
|
183
|
+
sys.exit(main())
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Shared agent configuration \u2014 skills for AI coding tools (Claude Code, Augment, Cursor, Cline, Windsurf, Gemini CLI).",
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.9.0",
|
|
10
10
|
"keywords": [
|
|
11
11
|
"agent-config",
|
|
12
12
|
"skills",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"strict": false,
|
|
43
43
|
"skills": [
|
|
44
44
|
"./.claude/skills/accessibility-auditor",
|
|
45
|
+
"./.claude/skills/activation-design",
|
|
45
46
|
"./.claude/skills/adr-create",
|
|
46
47
|
"./.claude/skills/adversarial-review",
|
|
47
48
|
"./.claude/skills/agent-docs-writing",
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
"./.claude/skills/bug-analyzer",
|
|
70
71
|
"./.claude/skills/bug-fix",
|
|
71
72
|
"./.claude/skills/bug-investigate",
|
|
73
|
+
"./.claude/skills/build-buy-partner",
|
|
72
74
|
"./.claude/skills/challenge-me",
|
|
73
75
|
"./.claude/skills/challenge-me-vision",
|
|
74
76
|
"./.claude/skills/challenge-me-with-docs",
|
|
@@ -78,20 +80,25 @@
|
|
|
78
80
|
"./.claude/skills/chat-history-show",
|
|
79
81
|
"./.claude/skills/check-current-md",
|
|
80
82
|
"./.claude/skills/check-refs",
|
|
83
|
+
"./.claude/skills/churn-prevention",
|
|
81
84
|
"./.claude/skills/code-refactoring",
|
|
82
85
|
"./.claude/skills/code-review",
|
|
83
86
|
"./.claude/skills/command-routing",
|
|
84
87
|
"./.claude/skills/command-writing",
|
|
85
88
|
"./.claude/skills/commit",
|
|
86
89
|
"./.claude/skills/commit-in-chunks",
|
|
87
|
-
"./.claude/skills/
|
|
90
|
+
"./.claude/skills/comp-banding",
|
|
91
|
+
"./.claude/skills/competitive-moat-analysis",
|
|
88
92
|
"./.claude/skills/competitive-positioning",
|
|
93
|
+
"./.claude/skills/composer-packages",
|
|
89
94
|
"./.claude/skills/compress",
|
|
95
|
+
"./.claude/skills/content-funnel-design",
|
|
90
96
|
"./.claude/skills/context",
|
|
91
97
|
"./.claude/skills/context-authoring",
|
|
92
98
|
"./.claude/skills/context-create",
|
|
93
99
|
"./.claude/skills/context-document",
|
|
94
100
|
"./.claude/skills/context-refactor",
|
|
101
|
+
"./.claude/skills/contracts-cognition",
|
|
95
102
|
"./.claude/skills/conventional-commits-writing",
|
|
96
103
|
"./.claude/skills/copilot-agents-optimization",
|
|
97
104
|
"./.claude/skills/copilot-config",
|
|
@@ -106,8 +113,10 @@
|
|
|
106
113
|
"./.claude/skills/customer-research",
|
|
107
114
|
"./.claude/skills/dashboard-design",
|
|
108
115
|
"./.claude/skills/data-flow-mapper",
|
|
116
|
+
"./.claude/skills/data-handling-judgment",
|
|
109
117
|
"./.claude/skills/database",
|
|
110
118
|
"./.claude/skills/dcf-modeling",
|
|
119
|
+
"./.claude/skills/deal-qualification-meddic",
|
|
111
120
|
"./.claude/skills/decision-record",
|
|
112
121
|
"./.claude/skills/deep-reading-analyst",
|
|
113
122
|
"./.claude/skills/defense-in-depth",
|
|
@@ -121,10 +130,12 @@
|
|
|
121
130
|
"./.claude/skills/dto-creator",
|
|
122
131
|
"./.claude/skills/e2e-heal",
|
|
123
132
|
"./.claude/skills/e2e-plan",
|
|
133
|
+
"./.claude/skills/editorial-calendar",
|
|
124
134
|
"./.claude/skills/eloquent",
|
|
125
135
|
"./.claude/skills/error-handling-patterns",
|
|
126
136
|
"./.claude/skills/estimate-ticket",
|
|
127
137
|
"./.claude/skills/existing-ui-audit",
|
|
138
|
+
"./.claude/skills/expansion-playbook",
|
|
128
139
|
"./.claude/skills/fe-design",
|
|
129
140
|
"./.claude/skills/feature",
|
|
130
141
|
"./.claude/skills/feature-dev",
|
|
@@ -144,13 +155,18 @@
|
|
|
144
155
|
"./.claude/skills/fix-refs",
|
|
145
156
|
"./.claude/skills/fix-seeder",
|
|
146
157
|
"./.claude/skills/flux",
|
|
158
|
+
"./.claude/skills/forecast-accuracy",
|
|
159
|
+
"./.claude/skills/forecasting",
|
|
147
160
|
"./.claude/skills/form-handler",
|
|
161
|
+
"./.claude/skills/fundraising-narrative",
|
|
148
162
|
"./.claude/skills/funnel-analysis",
|
|
149
163
|
"./.claude/skills/git-workflow",
|
|
150
164
|
"./.claude/skills/github-ci",
|
|
151
165
|
"./.claude/skills/grafana",
|
|
152
166
|
"./.claude/skills/grill-me",
|
|
167
|
+
"./.claude/skills/gtm-launch",
|
|
153
168
|
"./.claude/skills/guideline-writing",
|
|
169
|
+
"./.claude/skills/hiring-loop-design",
|
|
154
170
|
"./.claude/skills/implement-ticket",
|
|
155
171
|
"./.claude/skills/incident-commander",
|
|
156
172
|
"./.claude/skills/jira-integration",
|
|
@@ -180,6 +196,7 @@
|
|
|
180
196
|
"./.claude/skills/livewire",
|
|
181
197
|
"./.claude/skills/livewire-architect",
|
|
182
198
|
"./.claude/skills/logging-monitoring",
|
|
199
|
+
"./.claude/skills/market-entry-analysis",
|
|
183
200
|
"./.claude/skills/markitdown",
|
|
184
201
|
"./.claude/skills/mcp",
|
|
185
202
|
"./.claude/skills/mcp-builder",
|
|
@@ -192,6 +209,7 @@
|
|
|
192
209
|
"./.claude/skills/memory-promote",
|
|
193
210
|
"./.claude/skills/memory-propose",
|
|
194
211
|
"./.claude/skills/merge-conflicts",
|
|
212
|
+
"./.claude/skills/messaging-architecture",
|
|
195
213
|
"./.claude/skills/migration-architect",
|
|
196
214
|
"./.claude/skills/migration-creator",
|
|
197
215
|
"./.claude/skills/mobile-e2e-strategy",
|
|
@@ -203,6 +221,9 @@
|
|
|
203
221
|
"./.claude/skills/multi-tenancy",
|
|
204
222
|
"./.claude/skills/okr-tree-modeling",
|
|
205
223
|
"./.claude/skills/onboard",
|
|
224
|
+
"./.claude/skills/onboarding-design",
|
|
225
|
+
"./.claude/skills/onboarding-program",
|
|
226
|
+
"./.claude/skills/one-on-one-cadence",
|
|
206
227
|
"./.claude/skills/openapi",
|
|
207
228
|
"./.claude/skills/optimize",
|
|
208
229
|
"./.claude/skills/optimize-agents-dir",
|
|
@@ -211,12 +232,14 @@
|
|
|
211
232
|
"./.claude/skills/optimize-rtk",
|
|
212
233
|
"./.claude/skills/optimize-skills",
|
|
213
234
|
"./.claude/skills/orchestrate",
|
|
235
|
+
"./.claude/skills/org-design",
|
|
214
236
|
"./.claude/skills/override",
|
|
215
237
|
"./.claude/skills/override-create",
|
|
216
238
|
"./.claude/skills/override-manage",
|
|
217
239
|
"./.claude/skills/override-management",
|
|
218
240
|
"./.claude/skills/package-reset",
|
|
219
241
|
"./.claude/skills/package-test",
|
|
242
|
+
"./.claude/skills/perf-feedback-craft",
|
|
220
243
|
"./.claude/skills/performance",
|
|
221
244
|
"./.claude/skills/performance-analysis",
|
|
222
245
|
"./.claude/skills/persona-writing",
|
|
@@ -224,10 +247,13 @@
|
|
|
224
247
|
"./.claude/skills/php-coder",
|
|
225
248
|
"./.claude/skills/php-debugging",
|
|
226
249
|
"./.claude/skills/php-service",
|
|
250
|
+
"./.claude/skills/pipeline-strategy",
|
|
227
251
|
"./.claude/skills/playwright-architect",
|
|
228
252
|
"./.claude/skills/playwright-testing",
|
|
229
253
|
"./.claude/skills/po-discovery",
|
|
254
|
+
"./.claude/skills/positioning-strategy",
|
|
230
255
|
"./.claude/skills/prepare-for-review",
|
|
256
|
+
"./.claude/skills/privacy-review",
|
|
231
257
|
"./.claude/skills/project-analysis-core",
|
|
232
258
|
"./.claude/skills/project-analysis-hypothesis-driven",
|
|
233
259
|
"./.claude/skills/project-analysis-laravel",
|
|
@@ -258,6 +284,7 @@
|
|
|
258
284
|
"./.claude/skills/research",
|
|
259
285
|
"./.claude/skills/research-deep",
|
|
260
286
|
"./.claude/skills/research-report",
|
|
287
|
+
"./.claude/skills/retention-loops",
|
|
261
288
|
"./.claude/skills/review-changes",
|
|
262
289
|
"./.claude/skills/review-routing",
|
|
263
290
|
"./.claude/skills/rice-prioritization",
|
|
@@ -273,6 +300,8 @@
|
|
|
273
300
|
"./.claude/skills/rtk-output-filtering",
|
|
274
301
|
"./.claude/skills/rule-compliance-audit",
|
|
275
302
|
"./.claude/skills/rule-writing",
|
|
303
|
+
"./.claude/skills/runway-cognition",
|
|
304
|
+
"./.claude/skills/scenario-modeling",
|
|
276
305
|
"./.claude/skills/script-writing",
|
|
277
306
|
"./.claude/skills/secrets-management",
|
|
278
307
|
"./.claude/skills/security",
|
|
@@ -304,6 +333,7 @@
|
|
|
304
333
|
"./.claude/skills/tests-execute",
|
|
305
334
|
"./.claude/skills/threat-model",
|
|
306
335
|
"./.claude/skills/threat-modeling",
|
|
336
|
+
"./.claude/skills/throughput-vs-morale-tradeoff",
|
|
307
337
|
"./.claude/skills/token-optimizer",
|
|
308
338
|
"./.claude/skills/traefik",
|
|
309
339
|
"./.claude/skills/ui-component-architect",
|
|
@@ -314,7 +344,9 @@
|
|
|
314
344
|
"./.claude/skills/using-git-worktrees",
|
|
315
345
|
"./.claude/skills/validate-feature-fit",
|
|
316
346
|
"./.claude/skills/verify-completion-evidence",
|
|
347
|
+
"./.claude/skills/vision-articulation",
|
|
317
348
|
"./.claude/skills/voc-extract",
|
|
349
|
+
"./.claude/skills/voice-and-tone-design",
|
|
318
350
|
"./.claude/skills/websocket",
|
|
319
351
|
"./.claude/skills/work"
|
|
320
352
|
]
|
package/AGENTS.md
CHANGED
|
@@ -20,7 +20,7 @@ task ci # full pipeline — green before PR
|
|
|
20
20
|
- **Kernel + Router** (beta) — 9 always-loaded Iron-Law rules, tier-1 / tier-2 routing, cost profiles, per-rule char caps enforced by `task lint-rule-budget`: [`kernel-membership`](docs/contracts/kernel-membership.md) + [`rule-router`](docs/contracts/rule-router.md).
|
|
21
21
|
- **Content pipelines** — A [compression](docs/architecture/compression.md), B [Augment projection](docs/architecture/augment-projection.md), C [multi-tool projection](docs/architecture/multi-tool-projection.md), D [Claude.ai bundle](docs/architecture/claude-bundle.md). Index: [`docs/architecture.md`](docs/architecture.md).
|
|
22
22
|
- **Editing this repo** — Iron-Law rules (portability, source-of-truth, skill-quality) + Thin-Root contract: [`augment-portability`](.agent-src/rules/augment-portability.md), [`augment-source-of-truth`](.agent-src/rules/augment-source-of-truth.md), [`skill-quality`](.agent-src/rules/skill-quality.md), [`agents-md-thin-root`](.agent-src/skills/agents-md-thin-root/SKILL.md).
|
|
23
|
-
- **Consumer story** — `
|
|
23
|
+
- **Consumer story** — `npx` + `scripts/install.sh` + `.agent-settings.yml` opt-in flags, sandbox/offline install paths, verified-offline manifest: [`README.md`](README.md).
|
|
24
24
|
- **Personas** — 11 review-lens cast (6 core · 5 specialist), `personas:` vs `/mode` axes, citation map, override pattern: [`docs/personas.md`](docs/personas.md), schema [`docs/contracts/persona-schema.md`](docs/contracts/persona-schema.md) (beta).
|
|
25
25
|
|
|
26
26
|
## Emergency triage — read this when nothing else is reachable
|