@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.
Files changed (106) hide show
  1. package/.agent-src/commands/ghostwriter/delete.md +118 -0
  2. package/.agent-src/commands/ghostwriter/fetch.md +185 -0
  3. package/.agent-src/commands/ghostwriter/list.md +102 -0
  4. package/.agent-src/commands/ghostwriter/show.md +113 -0
  5. package/.agent-src/commands/ghostwriter/write.md +160 -0
  6. package/.agent-src/commands/ghostwriter.md +96 -0
  7. package/.agent-src/commands/post-as/ghostwriter.md +66 -0
  8. package/.agent-src/commands/post-as/me.md +124 -0
  9. package/.agent-src/commands/post-as.md +58 -0
  10. package/.agent-src/ghostwriter/README.md +61 -0
  11. package/.agent-src/ghostwriter/fictional-fixture-v1.md +94 -0
  12. package/.agent-src/personas/README.md +8 -0
  13. package/.agent-src/rules/domain-safety-disclaimer-consulting.md +52 -0
  14. package/.agent-src/rules/domain-safety-disclaimer-financial.md +54 -0
  15. package/.agent-src/rules/domain-safety-disclaimer-legal.md +49 -0
  16. package/.agent-src/rules/domain-safety-disclaimer-medical.md +56 -0
  17. package/.agent-src/rules/domain-safety-export-redact.md +65 -0
  18. package/.agent-src/rules/domain-safety-logging-pii-floor.md +55 -0
  19. package/.agent-src/rules/domain-safety-pii-finance.md +57 -0
  20. package/.agent-src/rules/domain-safety-pii-marketing.md +60 -0
  21. package/.agent-src/rules/domain-safety-pii-recruiting.md +56 -0
  22. package/.agent-src/rules/domain-safety-pii-support.md +57 -0
  23. package/.agent-src/rules/domain-safety-retention-finance.md +48 -0
  24. package/.agent-src/rules/domain-safety-retention-support.md +55 -0
  25. package/.agent-src/skills/api-design/SKILL.md +3 -0
  26. package/.agent-src/skills/authz-review/SKILL.md +3 -0
  27. package/.agent-src/skills/competitive-moat-analysis/SKILL.md +3 -0
  28. package/.agent-src/skills/competitive-positioning/SKILL.md +3 -0
  29. package/.agent-src/skills/content-funnel-design/SKILL.md +3 -0
  30. package/.agent-src/skills/contracts-cognition/SKILL.md +3 -0
  31. package/.agent-src/skills/dashboard-design/SKILL.md +3 -0
  32. package/.agent-src/skills/data-handling-judgment/SKILL.md +3 -0
  33. package/.agent-src/skills/dcf-modeling/SKILL.md +3 -0
  34. package/.agent-src/skills/deal-qualification-meddic/SKILL.md +3 -0
  35. package/.agent-src/skills/discovery-interview/SKILL.md +3 -0
  36. package/.agent-src/skills/editorial-calendar/SKILL.md +3 -0
  37. package/.agent-src/skills/forecast-accuracy/SKILL.md +3 -0
  38. package/.agent-src/skills/forecasting/SKILL.md +3 -0
  39. package/.agent-src/skills/fundraising-narrative/SKILL.md +3 -0
  40. package/.agent-src/skills/gtm-launch/SKILL.md +3 -0
  41. package/.agent-src/skills/incident-commander/SKILL.md +3 -0
  42. package/.agent-src/skills/launch-readiness/SKILL.md +3 -0
  43. package/.agent-src/skills/messaging-architecture/SKILL.md +3 -0
  44. package/.agent-src/skills/okr-tree-modeling/SKILL.md +3 -0
  45. package/.agent-src/skills/pipeline-strategy/SKILL.md +3 -0
  46. package/.agent-src/skills/playwright-architect/SKILL.md +3 -0
  47. package/.agent-src/skills/privacy-review/SKILL.md +4 -1
  48. package/.agent-src/skills/quality-tools/SKILL.md +3 -0
  49. package/.agent-src/skills/release-comms/SKILL.md +3 -0
  50. package/.agent-src/skills/runway-cognition/SKILL.md +3 -0
  51. package/.agent-src/skills/scenario-modeling/SKILL.md +3 -0
  52. package/.agent-src/skills/secrets-management/SKILL.md +3 -0
  53. package/.agent-src/skills/tech-debt-tracker/SKILL.md +3 -0
  54. package/.agent-src/skills/unit-economics-modeling/SKILL.md +3 -0
  55. package/.agent-src/skills/voc-extract/SKILL.md +3 -0
  56. package/.agent-src/skills/voice-and-tone-design/SKILL.md +3 -0
  57. package/.agent-src/templates/agents/agent-project-settings.example.yml +16 -1
  58. package/.agent-src/templates/scripts/work_engine/_lib/agent_settings.py +299 -20
  59. package/.claude-plugin/marketplace.json +10 -1
  60. package/CHANGELOG.md +200 -211
  61. package/README.md +55 -23
  62. package/config/gitignore-block.txt +8 -0
  63. package/docs/announcements/2026-05-non-dev-launch.md +79 -0
  64. package/docs/architecture.md +2 -2
  65. package/docs/archive/CHANGELOG-pre-2.15.0.md +244 -0
  66. package/docs/case-studies/_template.md +60 -0
  67. package/docs/catalog.md +24 -3
  68. package/docs/contracts/agent-user-schema.md +1 -0
  69. package/docs/contracts/command-clusters.md +2 -0
  70. package/docs/contracts/file-ownership-matrix.json +490 -0
  71. package/docs/contracts/ghostwriter-schema.md +337 -0
  72. package/docs/contracts/init-telemetry.md +133 -0
  73. package/docs/contracts/router-blending.md +71 -0
  74. package/docs/contracts/universal-skills.md +92 -0
  75. package/docs/contracts/write-engine.md +142 -0
  76. package/docs/getting-started-by-role.md +89 -0
  77. package/docs/getting-started-laravel.md +72 -0
  78. package/docs/getting-started.md +2 -2
  79. package/docs/installation.md +221 -2
  80. package/docs/safety.md +30 -0
  81. package/package.json +1 -1
  82. package/scripts/_cli/cmd_doctor.py +238 -8
  83. package/scripts/_cli/cmd_migrate.py +6 -1
  84. package/scripts/_cli/cmd_prune.py +8 -3
  85. package/scripts/_cli/cmd_sync.py +7 -3
  86. package/scripts/_cli/cmd_uninstall.py +4 -3
  87. package/scripts/_cli/cmd_update.py +5 -1
  88. package/scripts/_cli/cmd_validate.py +6 -3
  89. package/scripts/_cli/cmd_versions.py +15 -2
  90. package/scripts/_lib/agent_settings.py +299 -20
  91. package/scripts/agent-config +64 -0
  92. package/scripts/bench_runner.py +158 -0
  93. package/scripts/check_role_doc_links.py +110 -0
  94. package/scripts/compress.py +11 -0
  95. package/scripts/ghostwriter_fixture_allowlist.txt +16 -0
  96. package/scripts/install +39 -2
  97. package/scripts/install.py +304 -1
  98. package/scripts/install.sh +20 -0
  99. package/scripts/lint_ghostwriter_source.py +240 -0
  100. package/scripts/measure_skill_reduction.py +102 -0
  101. package/scripts/schemas/rule.schema.json +5 -0
  102. package/scripts/schemas/skill.schema.json +6 -0
  103. package/scripts/update-github-metadata.sh +84 -0
  104. package/templates/agent-config-wrapper.sh +7 -0
  105. package/templates/minimal/.agent-settings.yml +23 -0
  106. 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: ""
@@ -0,0 +1,2 @@
1
+ # Placeholder so `agents/` is committable in a fresh `--minimal` install.
2
+ # Rename / remove once you add real content under agents/ (roadmaps, contexts, …).