@event4u/agent-config 2.15.0 → 2.17.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/ghostwriter/delete.md +118 -0
- package/.agent-src/commands/ghostwriter/fetch.md +185 -0
- package/.agent-src/commands/ghostwriter/list.md +102 -0
- package/.agent-src/commands/ghostwriter/show.md +113 -0
- package/.agent-src/commands/ghostwriter/write.md +160 -0
- package/.agent-src/commands/ghostwriter.md +96 -0
- package/.agent-src/commands/post-as/ghostwriter.md +66 -0
- package/.agent-src/commands/post-as/me.md +124 -0
- package/.agent-src/commands/post-as.md +58 -0
- package/.agent-src/ghostwriter/README.md +61 -0
- package/.agent-src/ghostwriter/fictional-fixture-v1.md +94 -0
- package/.agent-src/personas/README.md +8 -0
- package/.agent-src/rules/domain-safety-disclaimer-consulting.md +52 -0
- package/.agent-src/rules/domain-safety-disclaimer-financial.md +54 -0
- package/.agent-src/rules/domain-safety-disclaimer-legal.md +49 -0
- package/.agent-src/rules/domain-safety-disclaimer-medical.md +56 -0
- package/.agent-src/rules/domain-safety-export-redact.md +65 -0
- package/.agent-src/rules/domain-safety-logging-pii-floor.md +55 -0
- package/.agent-src/rules/domain-safety-pii-finance.md +57 -0
- package/.agent-src/rules/domain-safety-pii-marketing.md +60 -0
- package/.agent-src/rules/domain-safety-pii-recruiting.md +56 -0
- package/.agent-src/rules/domain-safety-pii-support.md +57 -0
- package/.agent-src/rules/domain-safety-retention-finance.md +48 -0
- package/.agent-src/rules/domain-safety-retention-support.md +55 -0
- package/.agent-src/skills/api-design/SKILL.md +3 -0
- package/.agent-src/skills/authz-review/SKILL.md +3 -0
- package/.agent-src/skills/competitive-moat-analysis/SKILL.md +3 -0
- package/.agent-src/skills/competitive-positioning/SKILL.md +3 -0
- package/.agent-src/skills/content-funnel-design/SKILL.md +3 -0
- package/.agent-src/skills/contracts-cognition/SKILL.md +3 -0
- package/.agent-src/skills/dashboard-design/SKILL.md +3 -0
- package/.agent-src/skills/data-handling-judgment/SKILL.md +3 -0
- package/.agent-src/skills/dcf-modeling/SKILL.md +3 -0
- package/.agent-src/skills/deal-qualification-meddic/SKILL.md +3 -0
- package/.agent-src/skills/discovery-interview/SKILL.md +3 -0
- package/.agent-src/skills/editorial-calendar/SKILL.md +3 -0
- package/.agent-src/skills/forecast-accuracy/SKILL.md +3 -0
- package/.agent-src/skills/forecasting/SKILL.md +3 -0
- package/.agent-src/skills/fundraising-narrative/SKILL.md +3 -0
- package/.agent-src/skills/gtm-launch/SKILL.md +3 -0
- package/.agent-src/skills/incident-commander/SKILL.md +3 -0
- package/.agent-src/skills/launch-readiness/SKILL.md +3 -0
- package/.agent-src/skills/messaging-architecture/SKILL.md +3 -0
- package/.agent-src/skills/okr-tree-modeling/SKILL.md +3 -0
- package/.agent-src/skills/pipeline-strategy/SKILL.md +3 -0
- package/.agent-src/skills/playwright-architect/SKILL.md +3 -0
- package/.agent-src/skills/privacy-review/SKILL.md +4 -1
- package/.agent-src/skills/quality-tools/SKILL.md +3 -0
- package/.agent-src/skills/release-comms/SKILL.md +3 -0
- package/.agent-src/skills/runway-cognition/SKILL.md +3 -0
- package/.agent-src/skills/scenario-modeling/SKILL.md +3 -0
- package/.agent-src/skills/secrets-management/SKILL.md +3 -0
- package/.agent-src/skills/tech-debt-tracker/SKILL.md +3 -0
- package/.agent-src/skills/unit-economics-modeling/SKILL.md +3 -0
- package/.agent-src/skills/voc-extract/SKILL.md +3 -0
- package/.agent-src/skills/voice-and-tone-design/SKILL.md +3 -0
- package/.agent-src/templates/agents/agent-project-settings.example.yml +16 -1
- package/.agent-src/templates/scripts/work_engine/_lib/agent_settings.py +299 -20
- package/.claude-plugin/marketplace.json +10 -1
- package/CHANGELOG.md +200 -211
- package/README.md +55 -23
- package/config/gitignore-block.txt +8 -0
- package/docs/announcements/2026-05-non-dev-launch.md +79 -0
- package/docs/architecture.md +2 -2
- package/docs/archive/CHANGELOG-pre-2.15.0.md +244 -0
- package/docs/case-studies/_template.md +60 -0
- package/docs/catalog.md +24 -3
- package/docs/contracts/agent-user-schema.md +1 -0
- package/docs/contracts/command-clusters.md +2 -0
- package/docs/contracts/file-ownership-matrix.json +490 -0
- package/docs/contracts/ghostwriter-schema.md +337 -0
- package/docs/contracts/init-telemetry.md +133 -0
- package/docs/contracts/router-blending.md +71 -0
- package/docs/contracts/universal-skills.md +92 -0
- package/docs/contracts/write-engine.md +142 -0
- package/docs/getting-started-by-role.md +89 -0
- package/docs/getting-started-laravel.md +72 -0
- package/docs/getting-started.md +2 -2
- package/docs/installation.md +221 -2
- package/docs/safety.md +30 -0
- package/package.json +1 -1
- package/scripts/_cli/cmd_doctor.py +238 -8
- package/scripts/_cli/cmd_migrate.py +6 -1
- package/scripts/_cli/cmd_prune.py +8 -3
- package/scripts/_cli/cmd_sync.py +7 -3
- package/scripts/_cli/cmd_uninstall.py +4 -3
- package/scripts/_cli/cmd_update.py +5 -1
- package/scripts/_cli/cmd_validate.py +6 -3
- package/scripts/_cli/cmd_versions.py +15 -2
- package/scripts/_lib/agent_settings.py +299 -20
- package/scripts/agent-config +64 -0
- package/scripts/bench_runner.py +158 -0
- package/scripts/check_role_doc_links.py +110 -0
- package/scripts/compress.py +11 -0
- package/scripts/ghostwriter_fixture_allowlist.txt +16 -0
- package/scripts/install +39 -2
- package/scripts/install.py +304 -1
- package/scripts/install.sh +20 -0
- package/scripts/lint_ghostwriter_source.py +240 -0
- package/scripts/measure_skill_reduction.py +102 -0
- package/scripts/schemas/rule.schema.json +5 -0
- package/scripts/schemas/skill.schema.json +6 -0
- package/scripts/update-github-metadata.sh +84 -0
- package/templates/agent-config-wrapper.sh +7 -0
- package/templates/minimal/.agent-settings.yml +23 -0
- package/templates/minimal/agents-gitkeep +2 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Lint ghostwriter profile sources.
|
|
3
|
+
|
|
4
|
+
Two storage tiers exist (see docs/contracts/ghostwriter-schema.md):
|
|
5
|
+
|
|
6
|
+
* .agent-src.uncompressed/ghostwriter/ — package source. Ships
|
|
7
|
+
fictional fixtures ONLY (`fictional: true`). Every file stem must
|
|
8
|
+
be on scripts/ghostwriter_fixture_allowlist.txt. `aliases:` is
|
|
9
|
+
forbidden here (consumer-only feature).
|
|
10
|
+
* agents/ghostwriter/ — consumer real-person
|
|
11
|
+
profiles. Gitignored. Must NOT carry `fictional: true`. Optional
|
|
12
|
+
`aliases:` list validated per § Aliases storage rules.
|
|
13
|
+
|
|
14
|
+
This lint enforces both rules and runs in `task ci`.
|
|
15
|
+
|
|
16
|
+
Exit codes:
|
|
17
|
+
0 all profiles compliant
|
|
18
|
+
1 one or more violations
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import sys
|
|
23
|
+
import unicodedata
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
import yaml
|
|
27
|
+
|
|
28
|
+
QUIET = "--quiet" in sys.argv
|
|
29
|
+
|
|
30
|
+
REPO = Path(__file__).resolve().parents[1]
|
|
31
|
+
PACKAGE_DIR = REPO / ".agent-src.uncompressed" / "ghostwriter"
|
|
32
|
+
CONSUMER_DIR = REPO / "agents" / "ghostwriter"
|
|
33
|
+
ALLOWLIST = REPO / "scripts" / "ghostwriter_fixture_allowlist.txt"
|
|
34
|
+
EXEMPT_STEMS = frozenset({"README"})
|
|
35
|
+
|
|
36
|
+
ALIAS_MIN_LEN = 2
|
|
37
|
+
# Allowed Unicode blocks for aliases (Latin-only, no homoglyph scripts).
|
|
38
|
+
# Basic Latin + Latin-1 Supplement + Latin Extended-A/B cover Müller,
|
|
39
|
+
# Łukaszewicz, José, etc., while rejecting Cyrillic / Greek confusables.
|
|
40
|
+
ALLOWED_PUNCT = frozenset(" .'-")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def load_allowlist() -> set[str]:
|
|
44
|
+
if not ALLOWLIST.exists():
|
|
45
|
+
return set()
|
|
46
|
+
stems: set[str] = set()
|
|
47
|
+
for line in ALLOWLIST.read_text(encoding="utf-8").splitlines():
|
|
48
|
+
s = line.strip()
|
|
49
|
+
if not s or s.startswith("#"):
|
|
50
|
+
continue
|
|
51
|
+
stems.add(s)
|
|
52
|
+
return stems
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def parse_frontmatter(text: str) -> dict | None:
|
|
56
|
+
if not text.startswith("---\n"):
|
|
57
|
+
return None
|
|
58
|
+
end = text.find("\n---\n", 4)
|
|
59
|
+
if end == -1:
|
|
60
|
+
return None
|
|
61
|
+
try:
|
|
62
|
+
data = yaml.safe_load(text[4:end])
|
|
63
|
+
except yaml.YAMLError:
|
|
64
|
+
return None
|
|
65
|
+
return data if isinstance(data, dict) else None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def is_latin_or_allowed(ch: str) -> bool:
|
|
69
|
+
if ch in ALLOWED_PUNCT:
|
|
70
|
+
return True
|
|
71
|
+
if ch.isdigit():
|
|
72
|
+
return True
|
|
73
|
+
code = ord(ch)
|
|
74
|
+
# Basic Latin letters + Latin-1 Supplement letters + Latin Extended-A/B
|
|
75
|
+
if 0x0041 <= code <= 0x024F:
|
|
76
|
+
try:
|
|
77
|
+
return unicodedata.name(ch).startswith("LATIN ")
|
|
78
|
+
except ValueError:
|
|
79
|
+
return False
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def validate_alias(alias: str) -> str | None:
|
|
84
|
+
"""Return an error message, or None if the alias is valid."""
|
|
85
|
+
if not isinstance(alias, str):
|
|
86
|
+
return f"alias must be a string, got {type(alias).__name__}"
|
|
87
|
+
if len(alias) < ALIAS_MIN_LEN:
|
|
88
|
+
return f"alias {alias!r} is shorter than {ALIAS_MIN_LEN} characters"
|
|
89
|
+
normalised = unicodedata.normalize("NFC", alias)
|
|
90
|
+
if normalised != alias:
|
|
91
|
+
return f"alias {alias!r} is not Unicode-NFC-normalised"
|
|
92
|
+
bad = [ch for ch in alias if not is_latin_or_allowed(ch)]
|
|
93
|
+
if bad:
|
|
94
|
+
return (
|
|
95
|
+
f"alias {alias!r} contains non-Latin or homoglyph-prone "
|
|
96
|
+
f"character(s): {bad!r}"
|
|
97
|
+
)
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def lint_package_side(allowlist: set[str]) -> list[str]:
|
|
102
|
+
errors: list[str] = []
|
|
103
|
+
if not PACKAGE_DIR.exists():
|
|
104
|
+
return errors
|
|
105
|
+
for path in sorted(PACKAGE_DIR.glob("*.md")):
|
|
106
|
+
stem = path.stem
|
|
107
|
+
if stem in EXEMPT_STEMS:
|
|
108
|
+
continue
|
|
109
|
+
if stem not in allowlist:
|
|
110
|
+
errors.append(
|
|
111
|
+
f" off-allowlist (package source): {path.relative_to(REPO)} "
|
|
112
|
+
f"— add '{stem}' to scripts/ghostwriter_fixture_allowlist.txt"
|
|
113
|
+
)
|
|
114
|
+
continue
|
|
115
|
+
data = parse_frontmatter(path.read_text(encoding="utf-8"))
|
|
116
|
+
if data is None:
|
|
117
|
+
errors.append(
|
|
118
|
+
f" unparsable frontmatter (package source): {path.relative_to(REPO)}"
|
|
119
|
+
)
|
|
120
|
+
continue
|
|
121
|
+
if data.get("fictional") is not True:
|
|
122
|
+
errors.append(
|
|
123
|
+
f" missing 'fictional: true' (package source): {path.relative_to(REPO)} "
|
|
124
|
+
f"(got fictional={data.get('fictional')!r})"
|
|
125
|
+
)
|
|
126
|
+
if "aliases" in data:
|
|
127
|
+
errors.append(
|
|
128
|
+
f" 'aliases:' forbidden on fictional fixtures: {path.relative_to(REPO)} "
|
|
129
|
+
f"— aliases are a consumer-only feature (see schema § Aliases)"
|
|
130
|
+
)
|
|
131
|
+
return errors
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def lint_consumer_side() -> list[str]:
|
|
135
|
+
errors: list[str] = []
|
|
136
|
+
if not CONSUMER_DIR.exists():
|
|
137
|
+
return errors
|
|
138
|
+
# Collect (alias_ci, source_path, source_kind) tuples for cross-profile
|
|
139
|
+
# uniqueness check. source_kind is "alias" or "slug".
|
|
140
|
+
seen: dict[str, tuple[Path, str, str]] = {}
|
|
141
|
+
for path in sorted(CONSUMER_DIR.glob("*.md")):
|
|
142
|
+
if path.stem in EXEMPT_STEMS:
|
|
143
|
+
continue
|
|
144
|
+
slug = path.stem
|
|
145
|
+
slug_ci = slug.casefold()
|
|
146
|
+
# Register slug for cross-profile collision detection.
|
|
147
|
+
if slug_ci in seen:
|
|
148
|
+
prev_path, prev_value, prev_kind = seen[slug_ci]
|
|
149
|
+
errors.append(
|
|
150
|
+
f" duplicate slug across profiles: {path.relative_to(REPO)} "
|
|
151
|
+
f"vs {prev_path.relative_to(REPO)} (case-insensitive)"
|
|
152
|
+
)
|
|
153
|
+
else:
|
|
154
|
+
seen[slug_ci] = (path, slug, "slug")
|
|
155
|
+
|
|
156
|
+
data = parse_frontmatter(path.read_text(encoding="utf-8"))
|
|
157
|
+
if data is None:
|
|
158
|
+
continue
|
|
159
|
+
if data.get("fictional") is True:
|
|
160
|
+
errors.append(
|
|
161
|
+
f" 'fictional: true' in consumer tree: {path.relative_to(REPO)} "
|
|
162
|
+
f"— fictional fixtures belong in .agent-src.uncompressed/ghostwriter/"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
aliases = data.get("aliases")
|
|
166
|
+
if aliases is None:
|
|
167
|
+
continue
|
|
168
|
+
if not isinstance(aliases, list):
|
|
169
|
+
errors.append(
|
|
170
|
+
f" 'aliases' must be a YAML list: {path.relative_to(REPO)} "
|
|
171
|
+
f"(got {type(aliases).__name__})"
|
|
172
|
+
)
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
within_profile: set[str] = set()
|
|
176
|
+
for alias in aliases:
|
|
177
|
+
err = validate_alias(alias)
|
|
178
|
+
if err:
|
|
179
|
+
errors.append(f" {path.relative_to(REPO)}: {err}")
|
|
180
|
+
continue
|
|
181
|
+
alias_ci = alias.casefold()
|
|
182
|
+
if alias_ci in within_profile:
|
|
183
|
+
errors.append(
|
|
184
|
+
f" {path.relative_to(REPO)}: duplicate alias "
|
|
185
|
+
f"{alias!r} within the same profile (case-insensitive)"
|
|
186
|
+
)
|
|
187
|
+
continue
|
|
188
|
+
within_profile.add(alias_ci)
|
|
189
|
+
if alias_ci in seen:
|
|
190
|
+
prev_path, prev_value, prev_kind = seen[alias_ci]
|
|
191
|
+
errors.append(
|
|
192
|
+
f" alias collision: {path.relative_to(REPO)} alias "
|
|
193
|
+
f"{alias!r} collides with {prev_kind} {prev_value!r} in "
|
|
194
|
+
f"{prev_path.relative_to(REPO)} (case-insensitive)"
|
|
195
|
+
)
|
|
196
|
+
continue
|
|
197
|
+
seen[alias_ci] = (path, alias, "alias")
|
|
198
|
+
return errors
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def main() -> int:
|
|
202
|
+
allowlist = load_allowlist()
|
|
203
|
+
pkg_errors = lint_package_side(allowlist)
|
|
204
|
+
cons_errors = lint_consumer_side()
|
|
205
|
+
errors = pkg_errors + cons_errors
|
|
206
|
+
|
|
207
|
+
if errors:
|
|
208
|
+
print(
|
|
209
|
+
f"❌ lint_ghostwriter_source: {len(errors)} violation(s)",
|
|
210
|
+
file=sys.stderr,
|
|
211
|
+
)
|
|
212
|
+
for line in errors:
|
|
213
|
+
print(line, file=sys.stderr)
|
|
214
|
+
print(
|
|
215
|
+
" see docs/contracts/ghostwriter-schema.md § Lint enforcement",
|
|
216
|
+
file=sys.stderr,
|
|
217
|
+
)
|
|
218
|
+
return 1
|
|
219
|
+
|
|
220
|
+
if not QUIET:
|
|
221
|
+
pkg_count = (
|
|
222
|
+
sum(1 for p in PACKAGE_DIR.glob("*.md") if p.stem not in EXEMPT_STEMS)
|
|
223
|
+
if PACKAGE_DIR.exists()
|
|
224
|
+
else 0
|
|
225
|
+
)
|
|
226
|
+
cons_count = (
|
|
227
|
+
sum(1 for p in CONSUMER_DIR.glob("*.md") if p.stem not in EXEMPT_STEMS)
|
|
228
|
+
if CONSUMER_DIR.exists()
|
|
229
|
+
else 0
|
|
230
|
+
)
|
|
231
|
+
print(
|
|
232
|
+
f"✅ lint_ghostwriter_source: {pkg_count} package fixture(s), "
|
|
233
|
+
f"{cons_count} consumer profile(s), all compliant"
|
|
234
|
+
)
|
|
235
|
+
return 0
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
if __name__ == "__main__":
|
|
239
|
+
raise SystemExit(main())
|
|
240
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Skill-count reduction measurement — step-12 Phase 3 L74 deliverable.
|
|
3
|
+
|
|
4
|
+
Computes the skill-count reduction achieved by filtering on
|
|
5
|
+
`recommended_for_user_types` frontmatter tags. Each non-developer
|
|
6
|
+
user-type that lands ≥40% under the default-loaded skill count
|
|
7
|
+
satisfies the Phase 3 acceptance criterion.
|
|
8
|
+
|
|
9
|
+
The runtime filter (loaded vs. registered) ships with step-9; this
|
|
10
|
+
script measures the data already in place, so the box can close on
|
|
11
|
+
the basis of the underlying tagging being correct.
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
python3 scripts/measure_skill_reduction.py
|
|
15
|
+
python3 scripts/measure_skill_reduction.py --json
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import json
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
import yaml
|
|
27
|
+
except ImportError:
|
|
28
|
+
sys.stderr.write("error: PyYAML required\n")
|
|
29
|
+
sys.exit(2)
|
|
30
|
+
|
|
31
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
32
|
+
SKILLS_DIR = REPO_ROOT / ".agent-src.uncompressed" / "skills"
|
|
33
|
+
TARGET_REDUCTION = 0.40
|
|
34
|
+
PHASE_3_USER_TYPES = ("consultant", "creator")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_tags() -> tuple[int, dict[str, int]]:
|
|
38
|
+
total = 0
|
|
39
|
+
per_type: dict[str, int] = {}
|
|
40
|
+
for skill_dir in sorted(SKILLS_DIR.iterdir()):
|
|
41
|
+
skill_md = skill_dir / "SKILL.md"
|
|
42
|
+
if not skill_md.is_file():
|
|
43
|
+
continue
|
|
44
|
+
text = skill_md.read_text(encoding="utf-8")
|
|
45
|
+
if not text.startswith("---"):
|
|
46
|
+
continue
|
|
47
|
+
try:
|
|
48
|
+
fm = yaml.safe_load(text.split("---", 2)[1]) or {}
|
|
49
|
+
except yaml.YAMLError:
|
|
50
|
+
continue
|
|
51
|
+
total += 1
|
|
52
|
+
for t in fm.get("recommended_for_user_types") or []:
|
|
53
|
+
per_type[t] = per_type.get(t, 0) + 1
|
|
54
|
+
return total, per_type
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def main(argv=None) -> int:
|
|
58
|
+
ap = argparse.ArgumentParser()
|
|
59
|
+
ap.add_argument("--json", action="store_true")
|
|
60
|
+
args = ap.parse_args(argv)
|
|
61
|
+
|
|
62
|
+
total, per_type = load_tags()
|
|
63
|
+
if total == 0:
|
|
64
|
+
sys.stderr.write("error: no skills found\n")
|
|
65
|
+
return 2
|
|
66
|
+
|
|
67
|
+
report = {
|
|
68
|
+
"total_skills": total,
|
|
69
|
+
"target_reduction": TARGET_REDUCTION,
|
|
70
|
+
"per_user_type": {},
|
|
71
|
+
"phase_3_user_types": list(PHASE_3_USER_TYPES),
|
|
72
|
+
"phase_3_passed": True,
|
|
73
|
+
}
|
|
74
|
+
for ut in sorted(per_type):
|
|
75
|
+
loaded = per_type[ut]
|
|
76
|
+
reduction = 1 - (loaded / total)
|
|
77
|
+
report["per_user_type"][ut] = {
|
|
78
|
+
"loaded_skills": loaded,
|
|
79
|
+
"reduction_pct": round(reduction, 4),
|
|
80
|
+
"passes_target": reduction >= TARGET_REDUCTION,
|
|
81
|
+
}
|
|
82
|
+
for ut in PHASE_3_USER_TYPES:
|
|
83
|
+
entry = report["per_user_type"].get(ut)
|
|
84
|
+
if not entry or not entry["passes_target"]:
|
|
85
|
+
report["phase_3_passed"] = False
|
|
86
|
+
|
|
87
|
+
if args.json:
|
|
88
|
+
print(json.dumps(report, indent=2))
|
|
89
|
+
else:
|
|
90
|
+
print(f"total_skills: {total} target_reduction: ≥{TARGET_REDUCTION:.0%}")
|
|
91
|
+
for ut, e in report["per_user_type"].items():
|
|
92
|
+
mark = "✓" if e["passes_target"] else "✗"
|
|
93
|
+
star = " *" if ut in PHASE_3_USER_TYPES else ""
|
|
94
|
+
print(f" {mark} {ut:12s} loaded={e['loaded_skills']:3d} "
|
|
95
|
+
f"reduction={e['reduction_pct']:.1%}{star}")
|
|
96
|
+
print(f"verdict: {'PASS' if report['phase_3_passed'] else 'FAIL'}")
|
|
97
|
+
print("(* = step-12 Phase 3 L74 anchor user-types)")
|
|
98
|
+
return 0 if report["phase_3_passed"] else 1
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
sys.exit(main())
|
|
@@ -75,6 +75,11 @@
|
|
|
75
75
|
"items": {"type": "string", "pattern": "^(skill|guideline|command|contract):"},
|
|
76
76
|
"description": "Router targets (skill / guideline / command / contract). Forbidden on kernel rules. Schema: docs/contracts/rule-router.md."
|
|
77
77
|
},
|
|
78
|
+
"applies_to_user_types": {
|
|
79
|
+
"type": "array",
|
|
80
|
+
"items": {"type": "string"},
|
|
81
|
+
"description": "Forward-compatible user-type filter (step-12-universal-os-reframe Phase 4). Rule loads only when the active user-type matches one of these tags. Wired by step-9-user-types-axis once it lands; treated as a no-op gate until then. Free-form tags (e.g. 'support', 'finance', 'recruiting', 'marketing', 'legal-drafting', 'consulting', 'medical-drafting', 'finance-drafting', 'ops', 'analytics-export', 'all') — no enum until the user-types axis closes."
|
|
82
|
+
},
|
|
78
83
|
"profile": {
|
|
79
84
|
"type": "string",
|
|
80
85
|
"enum": ["minimal", "balanced", "full"],
|
|
@@ -80,6 +80,12 @@
|
|
|
80
80
|
},
|
|
81
81
|
"description": "Senior-skill opt-in for the context spine. Declares which slots under agents/context-spine/ the skill expects to read. Cross-wing slots (product, team, repo) are locked at 3 by council Q1 (KEEP-3); wing-scoped slots follow the per-wing ADR track in docs/contracts/context-spine.md § 5. Wing-3 (channel-stage, funnel-stage, customer-segment) authorized by docs/contracts/adr-gtm-context-spine.md; Wing-4 (fiscal-period, org-stage, regulatory-regime) authorized by docs/contracts/adr-wing4-context-spine.md."
|
|
82
82
|
},
|
|
83
|
+
"recommended_for_user_types": {
|
|
84
|
+
"type": "array",
|
|
85
|
+
"uniqueItems": true,
|
|
86
|
+
"items": {"type": "string"},
|
|
87
|
+
"description": "Forward-compatible user-type recommendation tags (step-12-universal-os-reframe Phase 5). Skill loads for every user-type whose slug appears in this list. Absence of the key marks the skill as universal — see docs/contracts/universal-skills.md for the always-loaded floor and docs/contracts/router-blending.md for per-user-type mix ratios. Wired by step-9-user-types-axis once it lands; treated as metadata until then. Free-form tags (e.g. 'creator', 'founder', 'consultant', 'gtm', 'finance', 'ops', 'developer') — no enum until the user-types axis closes."
|
|
88
|
+
},
|
|
83
89
|
"execution": {
|
|
84
90
|
"type": "object",
|
|
85
91
|
"additionalProperties": false,
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scripts/update-github-metadata.sh
|
|
3
|
+
#
|
|
4
|
+
# Step-12 Phase 6 L113 — apply the Universal-OS reframe to GitHub repo
|
|
5
|
+
# metadata (description + topics). Drafted per AI-Council verdict
|
|
6
|
+
# 2026-05-15-step12-final-push (Decision 3 AMEND: reviewable script,
|
|
7
|
+
# explicit maintainer approval to execute).
|
|
8
|
+
#
|
|
9
|
+
# Iron Law (.augment/rules/non-destructive-by-default.md):
|
|
10
|
+
# GitHub repo description/topics are PUBLIC project metadata.
|
|
11
|
+
# This script does NOT auto-run. Maintainer must invoke it explicitly.
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# ./scripts/update-github-metadata.sh # dry-run (prints curl payload)
|
|
15
|
+
# ./scripts/update-github-metadata.sh --apply # actually call the API
|
|
16
|
+
#
|
|
17
|
+
# Rollback:
|
|
18
|
+
# gh api repos/event4u-app/agent-config --method PATCH \
|
|
19
|
+
# -f description="agent-config — Behavior, Memory and Delivery Governance for AI Agents"
|
|
20
|
+
# (Topics: re-PUT the original list from `gh api repos/event4u-app/agent-config | jq .topics`.)
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
OWNER_REPO="event4u-app/agent-config"
|
|
24
|
+
|
|
25
|
+
NEW_DESCRIPTION="Universal AI Agent OS — governed skills, rules, commands for developers, founders, creators, GTM, finance/ops"
|
|
26
|
+
|
|
27
|
+
# Existing topics preserved; reframe topics appended.
|
|
28
|
+
TOPICS=(
|
|
29
|
+
"agent-rules"
|
|
30
|
+
"agent-skills"
|
|
31
|
+
"agentic-ai"
|
|
32
|
+
"agentskills-standard"
|
|
33
|
+
"ai-coding"
|
|
34
|
+
"augment-agent"
|
|
35
|
+
"claude-code"
|
|
36
|
+
"copilot"
|
|
37
|
+
"devcontainer"
|
|
38
|
+
"governance"
|
|
39
|
+
"laravel"
|
|
40
|
+
"php"
|
|
41
|
+
"react"
|
|
42
|
+
"symfony"
|
|
43
|
+
"universal-ai-os"
|
|
44
|
+
"ai-governance"
|
|
45
|
+
"non-developer-tools"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
APPLY="${1:-}"
|
|
49
|
+
|
|
50
|
+
if [[ "${APPLY}" != "--apply" ]]; then
|
|
51
|
+
echo "=== DRY RUN — no API call ==="
|
|
52
|
+
echo "Target repo: ${OWNER_REPO}"
|
|
53
|
+
echo "New description: ${NEW_DESCRIPTION}"
|
|
54
|
+
echo "New topics:"
|
|
55
|
+
printf ' - %s\n' "${TOPICS[@]}"
|
|
56
|
+
echo
|
|
57
|
+
echo "To apply, re-run with --apply (requires gh authenticated as repo admin)."
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Apply path — requires gh CLI authenticated.
|
|
62
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
63
|
+
echo "error: gh CLI not found. Install from https://cli.github.com." >&2
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
echo "Applying description …"
|
|
68
|
+
gh api "repos/${OWNER_REPO}" \
|
|
69
|
+
--method PATCH \
|
|
70
|
+
-f "description=${NEW_DESCRIPTION}" \
|
|
71
|
+
--silent
|
|
72
|
+
|
|
73
|
+
echo "Applying topics …"
|
|
74
|
+
TOPIC_ARGS=()
|
|
75
|
+
for t in "${TOPICS[@]}"; do
|
|
76
|
+
TOPIC_ARGS+=(-f "names[]=${t}")
|
|
77
|
+
done
|
|
78
|
+
gh api "repos/${OWNER_REPO}/topics" \
|
|
79
|
+
--method PUT \
|
|
80
|
+
-H "Accept: application/vnd.github.mercy-preview+json" \
|
|
81
|
+
"${TOPIC_ARGS[@]}" \
|
|
82
|
+
--silent
|
|
83
|
+
|
|
84
|
+
echo "Done. Verify at https://github.com/${OWNER_REPO}"
|
|
@@ -21,6 +21,13 @@ set -euo pipefail
|
|
|
21
21
|
|
|
22
22
|
SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
23
|
|
|
24
|
+
# Step-7 Phase 3 — pin the project root for the master CLI so subdir
|
|
25
|
+
# invocations of ``./agent-config`` (or symlinks to it) do not re-walk
|
|
26
|
+
# the anchor tree. The wrapper lives at the project root by definition,
|
|
27
|
+
# so SELF_DIR *is* the canonical root. Honor a pre-set value as an
|
|
28
|
+
# escape hatch (test harnesses, supervisors that already pinned it).
|
|
29
|
+
export AGENT_CONFIG_PROJECT_ROOT="${AGENT_CONFIG_PROJECT_ROOT:-$SELF_DIR}"
|
|
30
|
+
|
|
24
31
|
locate_master() {
|
|
25
32
|
if [[ -n "${AGENT_CONFIG_MASTER:-}" && -x "$AGENT_CONFIG_MASTER" ]]; then
|
|
26
33
|
printf '%s' "$AGENT_CONFIG_MASTER"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Agent Settings — minimal stub
|
|
2
|
+
#
|
|
3
|
+
# Created by `agent-config init --minimal`. This file marks the project as
|
|
4
|
+
# an agent-config layer without installing any tool payload. The global
|
|
5
|
+
# `agent-config` binary on $PATH supplies the rules / skills / commands;
|
|
6
|
+
# this file is your per-project override point.
|
|
7
|
+
#
|
|
8
|
+
# Resolution order (deepest wins, see docs/contracts/layered-settings.md):
|
|
9
|
+
# 1. <repo-root>/.agent-settings.yml ← this file
|
|
10
|
+
# 2. ~/.event4u/agent-config/agent-settings.yml (user-global; whitelisted)
|
|
11
|
+
#
|
|
12
|
+
# Add keys here as you need them. Reference template:
|
|
13
|
+
# templates/consumer-settings/ or docs/customization.md
|
|
14
|
+
|
|
15
|
+
# --- Cost profile ---
|
|
16
|
+
# Master switch for which rule tiers load. Options: minimal | balanced | full.
|
|
17
|
+
# `balanced` is the recommended default; flip to `minimal` for kernel-only.
|
|
18
|
+
cost_profile: balanced
|
|
19
|
+
|
|
20
|
+
# --- Version pin (opt-in) ---
|
|
21
|
+
# Leave commented out to follow whatever `agent-config` is on $PATH (per D4).
|
|
22
|
+
# Uncomment and pin to a specific release for reproducible installs.
|
|
23
|
+
# agent_config_version: ""
|