@event4u/agent-config 1.12.0 → 1.14.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 (260) hide show
  1. package/.agent-src/commands/agent-handoff.md +3 -0
  2. package/.agent-src/commands/agent-status.md +3 -0
  3. package/.agent-src/commands/agents-audit.md +4 -0
  4. package/.agent-src/commands/agents-cleanup.md +6 -1
  5. package/.agent-src/commands/agents-prepare.md +3 -0
  6. package/.agent-src/commands/analyze-reference-repo.md +4 -0
  7. package/.agent-src/commands/bug-fix.md +5 -1
  8. package/.agent-src/commands/bug-investigate.md +4 -0
  9. package/.agent-src/commands/chat-history-checkpoint.md +126 -0
  10. package/.agent-src/commands/chat-history-clear.md +5 -0
  11. package/.agent-src/commands/chat-history-resume.md +5 -0
  12. package/.agent-src/commands/chat-history.md +5 -0
  13. package/.agent-src/commands/check-current-md.md +126 -0
  14. package/.agent-src/commands/commit-in-chunks.md +98 -0
  15. package/.agent-src/commands/commit.md +4 -0
  16. package/.agent-src/commands/compress.md +3 -0
  17. package/.agent-src/commands/context-create.md +4 -0
  18. package/.agent-src/commands/context-refactor.md +4 -0
  19. package/.agent-src/commands/copilot-agents-init.md +3 -0
  20. package/.agent-src/commands/copilot-agents-optimize.md +3 -0
  21. package/.agent-src/commands/create-pr-description.md +4 -0
  22. package/.agent-src/commands/create-pr.md +4 -0
  23. package/.agent-src/commands/do-and-judge.md +4 -1
  24. package/.agent-src/commands/do-in-steps.md +3 -0
  25. package/.agent-src/commands/e2e-heal.md +4 -0
  26. package/.agent-src/commands/e2e-plan.md +4 -0
  27. package/.agent-src/commands/estimate-ticket.md +4 -1
  28. package/.agent-src/commands/feature-dev.md +4 -0
  29. package/.agent-src/commands/feature-explore.md +4 -0
  30. package/.agent-src/commands/feature-plan.md +4 -0
  31. package/.agent-src/commands/feature-refactor.md +4 -0
  32. package/.agent-src/commands/feature-roadmap.md +6 -0
  33. package/.agent-src/commands/fix-ci.md +4 -0
  34. package/.agent-src/commands/fix-portability.md +3 -0
  35. package/.agent-src/commands/fix-pr-bot-comments.md +4 -0
  36. package/.agent-src/commands/fix-pr-comments.md +4 -0
  37. package/.agent-src/commands/fix-pr-developer-comments.md +4 -0
  38. package/.agent-src/commands/fix-references.md +3 -0
  39. package/.agent-src/commands/fix-seeder.md +4 -0
  40. package/.agent-src/commands/implement-ticket.md +39 -13
  41. package/.agent-src/commands/jira-ticket.md +4 -0
  42. package/.agent-src/commands/judge.md +3 -0
  43. package/.agent-src/commands/memory-add.md +5 -3
  44. package/.agent-src/commands/memory-full.md +5 -2
  45. package/.agent-src/commands/memory-promote.md +7 -6
  46. package/.agent-src/commands/mode.md +3 -0
  47. package/.agent-src/commands/module-create.md +4 -0
  48. package/.agent-src/commands/module-explore.md +4 -0
  49. package/.agent-src/commands/onboard.md +24 -0
  50. package/.agent-src/commands/optimize-agents.md +4 -0
  51. package/.agent-src/commands/optimize-augmentignore.md +3 -0
  52. package/.agent-src/commands/optimize-rtk-filters.md +3 -0
  53. package/.agent-src/commands/optimize-skills.md +4 -0
  54. package/.agent-src/commands/override-create.md +4 -0
  55. package/.agent-src/commands/override-manage.md +4 -0
  56. package/.agent-src/commands/package-reset.md +3 -0
  57. package/.agent-src/commands/package-test.md +3 -0
  58. package/.agent-src/commands/prepare-for-review.md +4 -0
  59. package/.agent-src/commands/project-analyze.md +4 -0
  60. package/.agent-src/commands/project-health.md +4 -0
  61. package/.agent-src/commands/propose-memory.md +6 -8
  62. package/.agent-src/commands/quality-fix.md +4 -0
  63. package/.agent-src/commands/refine-ticket.md +4 -1
  64. package/.agent-src/commands/review-changes.md +4 -0
  65. package/.agent-src/commands/review-routing.md +4 -0
  66. package/.agent-src/commands/roadmap-create.md +7 -0
  67. package/.agent-src/commands/roadmap-execute.md +12 -1
  68. package/.agent-src/commands/rule-compliance-audit.md +4 -0
  69. package/.agent-src/commands/set-cost-profile.md +3 -0
  70. package/.agent-src/commands/sync-agent-settings.md +3 -0
  71. package/.agent-src/commands/sync-gitignore.md +3 -0
  72. package/.agent-src/commands/tests-create.md +4 -0
  73. package/.agent-src/commands/tests-execute.md +4 -0
  74. package/.agent-src/commands/threat-model.md +4 -0
  75. package/.agent-src/commands/update-form-request-messages.md +4 -0
  76. package/.agent-src/commands/upstream-contribute.md +4 -0
  77. package/.agent-src/commands/work.md +161 -0
  78. package/.agent-src/guidelines/agent-infra/engineering-memory-data-format.md +2 -6
  79. package/.agent-src/guidelines/agent-infra/layered-settings.md +0 -1
  80. package/.agent-src/guidelines/agent-infra/memory-access.md +0 -7
  81. package/.agent-src/guidelines/agent-infra/role-contracts.md +2 -4
  82. package/.agent-src/guidelines/agent-infra/self-improvement-pipeline.md +0 -1
  83. package/.agent-src/guidelines/php/patterns/strategy.md +180 -2
  84. package/.agent-src/personas/README.md +0 -1
  85. package/.agent-src/rules/artifact-drafting-protocol.md +7 -2
  86. package/.agent-src/rules/artifact-engagement-recording.md +133 -0
  87. package/.agent-src/rules/ask-when-uncertain.md +18 -13
  88. package/.agent-src/rules/augment-portability.md +8 -0
  89. package/.agent-src/rules/autonomous-execution.md +158 -0
  90. package/.agent-src/rules/chat-history.md +147 -118
  91. package/.agent-src/rules/cli-output-handling.md +26 -3
  92. package/.agent-src/rules/command-suggestion.md +133 -0
  93. package/.agent-src/rules/commit-policy.md +99 -0
  94. package/.agent-src/rules/direct-answers.md +114 -0
  95. package/.agent-src/rules/docs-sync.md +36 -0
  96. package/.agent-src/rules/downstream-changes.md +10 -9
  97. package/.agent-src/rules/improve-before-implement.md +9 -6
  98. package/.agent-src/rules/language-and-tone.md +81 -6
  99. package/.agent-src/rules/non-destructive-by-default.md +117 -0
  100. package/.agent-src/rules/package-ci-checks.md +4 -0
  101. package/.agent-src/rules/preservation-guard.md +20 -0
  102. package/.agent-src/rules/roadmap-progress-sync.md +103 -30
  103. package/.agent-src/rules/scope-control.md +42 -1
  104. package/.agent-src/rules/size-enforcement.md +1 -3
  105. package/.agent-src/rules/skill-quality.md +3 -8
  106. package/.agent-src/rules/ui-audit-before-build.md +106 -0
  107. package/.agent-src/rules/user-interaction.md +81 -3
  108. package/.agent-src/scripts/update_roadmap_progress.py +48 -6
  109. package/.agent-src/skills/blade-ui/SKILL.md +30 -5
  110. package/.agent-src/skills/command-routing/SKILL.md +32 -0
  111. package/.agent-src/skills/command-writing/SKILL.md +41 -2
  112. package/.agent-src/skills/description-assist/SKILL.md +21 -0
  113. package/.agent-src/skills/estimate-ticket/SKILL.md +0 -1
  114. package/.agent-src/skills/existing-ui-audit/SKILL.md +187 -0
  115. package/.agent-src/skills/fe-design/SKILL.md +72 -60
  116. package/.agent-src/skills/finishing-a-development-branch/SKILL.md +4 -0
  117. package/.agent-src/skills/flux/SKILL.md +31 -4
  118. package/.agent-src/skills/guideline-writing/SKILL.md +24 -2
  119. package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +51 -9
  120. package/.agent-src/skills/livewire/SKILL.md +30 -4
  121. package/.agent-src/skills/md-language-check/SKILL.md +103 -0
  122. package/.agent-src/skills/php-coder/SKILL.md +24 -0
  123. package/.agent-src/skills/react-shadcn-ui/SKILL.md +121 -0
  124. package/.agent-src/skills/refine-prompt/SKILL.md +220 -0
  125. package/.agent-src/skills/refine-ticket/SKILL.md +2 -4
  126. package/.agent-src/skills/roadmap-management/SKILL.md +10 -3
  127. package/.agent-src/skills/rule-writing/SKILL.md +23 -1
  128. package/.agent-src/skills/skill-writing/SKILL.md +1 -3
  129. package/.agent-src/skills/upstream-contribute/SKILL.md +1 -1
  130. package/.agent-src/skills/using-git-worktrees/SKILL.md +3 -1
  131. package/.agent-src/templates/AGENTS.md +24 -6
  132. package/.agent-src/templates/agent-settings.md +149 -0
  133. package/.agent-src/templates/github-workflows/roadmap-progress-check.yml +63 -0
  134. package/.agent-src/templates/hooks/pre-commit-roadmap-progress +60 -0
  135. package/.agent-src/templates/roadmaps.md +8 -2
  136. package/.agent-src/templates/scripts/implement_ticket/__init__.py +63 -26
  137. package/.agent-src/templates/scripts/implement_ticket/__main__.py +8 -2
  138. package/.agent-src/templates/scripts/memory_lookup.py +382 -21
  139. package/.agent-src/templates/scripts/memory_status.py +110 -9
  140. package/.agent-src/templates/scripts/telemetry/__init__.py +42 -0
  141. package/.agent-src/templates/scripts/telemetry/aggregator.py +154 -0
  142. package/.agent-src/templates/scripts/telemetry/boundary.py +171 -0
  143. package/.agent-src/templates/scripts/telemetry/engagement.py +238 -0
  144. package/.agent-src/templates/scripts/telemetry/report_renderer.py +170 -0
  145. package/.agent-src/templates/scripts/telemetry/settings.py +112 -0
  146. package/.agent-src/templates/scripts/telemetry_record.py +166 -0
  147. package/.agent-src/templates/scripts/telemetry_report.py +161 -0
  148. package/.agent-src/templates/scripts/telemetry_status.py +142 -0
  149. package/.agent-src/templates/scripts/work_engine/__init__.py +58 -0
  150. package/.agent-src/templates/scripts/work_engine/__main__.py +9 -0
  151. package/.agent-src/templates/scripts/work_engine/cli.py +592 -0
  152. package/.agent-src/templates/scripts/{implement_ticket → work_engine}/delivery_state.py +7 -0
  153. package/.agent-src/templates/scripts/work_engine/directives/__init__.py +33 -0
  154. package/.agent-src/templates/scripts/work_engine/directives/backend/__init__.py +98 -0
  155. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/analyze.py +1 -1
  156. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/implement.py +2 -2
  157. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/memory.py +1 -1
  158. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/plan.py +1 -1
  159. package/.agent-src/templates/scripts/work_engine/directives/backend/refine.py +396 -0
  160. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/report.py +36 -4
  161. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/test.py +2 -2
  162. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/verify.py +2 -2
  163. package/.agent-src/templates/scripts/work_engine/directives/mixed/__init__.py +116 -0
  164. package/.agent-src/templates/scripts/work_engine/directives/mixed/contract.py +254 -0
  165. package/.agent-src/templates/scripts/work_engine/directives/mixed/stitch.py +229 -0
  166. package/.agent-src/templates/scripts/work_engine/directives/mixed/ui.py +231 -0
  167. package/.agent-src/templates/scripts/work_engine/directives/ui/__init__.py +113 -0
  168. package/.agent-src/templates/scripts/work_engine/directives/ui/_passthrough.py +44 -0
  169. package/.agent-src/templates/scripts/work_engine/directives/ui/apply.py +241 -0
  170. package/.agent-src/templates/scripts/work_engine/directives/ui/audit.py +414 -0
  171. package/.agent-src/templates/scripts/work_engine/directives/ui/design.py +335 -0
  172. package/.agent-src/templates/scripts/work_engine/directives/ui/polish.py +510 -0
  173. package/.agent-src/templates/scripts/work_engine/directives/ui/review.py +468 -0
  174. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/__init__.py +119 -0
  175. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/_skipped.py +37 -0
  176. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/apply.py +165 -0
  177. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/refine.py +66 -0
  178. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/report.py +62 -0
  179. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/test.py +115 -0
  180. package/.agent-src/templates/scripts/work_engine/dispatcher.py +331 -0
  181. package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +54 -0
  182. package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +32 -0
  183. package/.agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +103 -0
  184. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +44 -0
  185. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +42 -0
  186. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_heartbeat.py +50 -0
  187. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_turn_check.py +49 -0
  188. package/.agent-src/templates/scripts/work_engine/hooks/builtin/directive_set_guard.py +53 -0
  189. package/.agent-src/templates/scripts/work_engine/hooks/builtin/halt_surface_audit.py +50 -0
  190. package/.agent-src/templates/scripts/work_engine/hooks/builtin/state_shape_validation.py +52 -0
  191. package/.agent-src/templates/scripts/work_engine/hooks/builtin/trace.py +84 -0
  192. package/.agent-src/templates/scripts/work_engine/hooks/context.py +66 -0
  193. package/.agent-src/templates/scripts/work_engine/hooks/events.py +44 -0
  194. package/.agent-src/templates/scripts/work_engine/hooks/exceptions.py +79 -0
  195. package/.agent-src/templates/scripts/work_engine/hooks/registry.py +60 -0
  196. package/.agent-src/templates/scripts/work_engine/hooks/runner.py +73 -0
  197. package/.agent-src/templates/scripts/work_engine/hooks/settings.py +141 -0
  198. package/.agent-src/templates/scripts/work_engine/intent/__init__.py +47 -0
  199. package/.agent-src/templates/scripts/work_engine/intent/classify.py +280 -0
  200. package/.agent-src/templates/scripts/work_engine/migration/__init__.py +8 -0
  201. package/.agent-src/templates/scripts/work_engine/migration/v0_to_v1.py +199 -0
  202. package/.agent-src/templates/scripts/work_engine/resolvers/__init__.py +22 -0
  203. package/.agent-src/templates/scripts/work_engine/resolvers/diff.py +106 -0
  204. package/.agent-src/templates/scripts/work_engine/resolvers/file.py +113 -0
  205. package/.agent-src/templates/scripts/work_engine/resolvers/prompt.py +90 -0
  206. package/.agent-src/templates/scripts/work_engine/scoring/__init__.py +14 -0
  207. package/.agent-src/templates/scripts/work_engine/scoring/confidence.py +300 -0
  208. package/.agent-src/templates/scripts/work_engine/stack/__init__.py +31 -0
  209. package/.agent-src/templates/scripts/work_engine/stack/detect.py +187 -0
  210. package/.agent-src/templates/scripts/work_engine/state.py +641 -0
  211. package/.claude-plugin/marketplace.json +105 -2
  212. package/AGENTS.md +36 -8
  213. package/CHANGELOG.md +558 -0
  214. package/README.md +146 -4
  215. package/composer.json +3 -0
  216. package/config/agent-settings.template.yml +45 -0
  217. package/config/gitignore-block.txt +4 -0
  218. package/docs/architecture.md +28 -1
  219. package/docs/development.md +1 -1
  220. package/docs/getting-started.md +3 -2
  221. package/docs/installation.md +86 -0
  222. package/docs/showcase.md +204 -0
  223. package/package.json +9 -1
  224. package/scripts/agent-config +274 -0
  225. package/scripts/audit_cloud_compatibility.py +288 -0
  226. package/scripts/build_cloud_bundle.py +458 -0
  227. package/scripts/build_linear_digest.py +263 -0
  228. package/scripts/chat_history.py +796 -7
  229. package/scripts/check_compression.py +139 -0
  230. package/scripts/check_iron_law_prominence.py +143 -0
  231. package/scripts/check_md_language.py +159 -0
  232. package/scripts/check_portability.py +36 -0
  233. package/scripts/check_reply_consistency.py +140 -0
  234. package/scripts/command_suggester/__init__.py +51 -0
  235. package/scripts/command_suggester/cooldown.py +132 -0
  236. package/scripts/command_suggester/loader.py +70 -0
  237. package/scripts/command_suggester/match.py +180 -0
  238. package/scripts/command_suggester/rank.py +120 -0
  239. package/scripts/command_suggester/render.py +86 -0
  240. package/scripts/command_suggester/sanitize.py +113 -0
  241. package/scripts/command_suggester/settings.py +125 -0
  242. package/scripts/command_suggester/types.py +78 -0
  243. package/scripts/hooks/augment-chat-history.sh +56 -0
  244. package/scripts/install-hooks.sh +67 -0
  245. package/scripts/install.py +150 -33
  246. package/scripts/lint_marketplace.py +27 -0
  247. package/scripts/memory_lookup.py +143 -7
  248. package/scripts/memory_status.py +76 -14
  249. package/scripts/migrate_command_suggestions.py +151 -0
  250. package/scripts/postinstall.sh +16 -0
  251. package/scripts/schemas/command.schema.json +41 -0
  252. package/scripts/skill_linter.py +67 -0
  253. package/scripts/sync_agent_settings.py +42 -12
  254. package/templates/consumer-settings/augment-cli-hooks.json +54 -0
  255. package/templates/consumer-settings/claude-settings.json +55 -1
  256. package/.agent-src/templates/scripts/implement_ticket/cli.py +0 -171
  257. package/.agent-src/templates/scripts/implement_ticket/dispatcher.py +0 -134
  258. package/.agent-src/templates/scripts/implement_ticket/steps/__init__.py +0 -49
  259. package/.agent-src/templates/scripts/implement_ticket/steps/refine.py +0 -140
  260. /package/.agent-src/templates/scripts/{implement_ticket → work_engine}/persona_policy.py +0 -0
@@ -10,6 +10,8 @@ shape used by anthropics/skills:
10
10
  - metadata must have description + version
11
11
  - metadata.version must match package.json (single source of truth)
12
12
  - every plugins[].skills[] entry must exist on disk and carry a SKILL.md
13
+ - every SKILL.md on disk under .claude/skills/ must be listed in some
14
+ plugin's skills[] (drift detection)
13
15
 
14
16
  Exit codes: 0 = clean, 1 = problems found, 3 = internal error.
15
17
  """
@@ -23,6 +25,7 @@ from pathlib import Path
23
25
  ROOT = Path(".")
24
26
  MARKETPLACE = ROOT / ".claude-plugin" / "marketplace.json"
25
27
  PACKAGE_JSON = ROOT / "package.json"
28
+ CLAUDE_SKILLS_DIR = ROOT / ".claude" / "skills"
26
29
 
27
30
 
28
31
  def fail(errors: list[str]) -> int:
@@ -121,6 +124,30 @@ def main() -> int:
121
124
  if not skill_md.exists():
122
125
  errors.append(f"{entry} has no SKILL.md: `{path}`")
123
126
 
127
+ # Reverse-completeness: every SKILL.md on disk under .claude/skills/
128
+ # must appear in some plugin's skills[]. Catches the drift where new
129
+ # skills are generated but never added to the marketplace manifest.
130
+ listed: set[str] = set()
131
+ for plugin in plugins:
132
+ if not isinstance(plugin, dict):
133
+ continue
134
+ for path in plugin.get("skills", []):
135
+ if isinstance(path, str):
136
+ listed.add(path.removeprefix("./"))
137
+
138
+ if CLAUDE_SKILLS_DIR.exists():
139
+ for skill_dir in sorted(CLAUDE_SKILLS_DIR.iterdir()):
140
+ if not skill_dir.is_dir():
141
+ continue
142
+ if not (skill_dir / "SKILL.md").exists():
143
+ continue
144
+ rel = f".claude/skills/{skill_dir.name}"
145
+ if rel not in listed:
146
+ errors.append(
147
+ f"skill exists on disk but is not listed in marketplace.json: "
148
+ f"`./{rel}`"
149
+ )
150
+
124
151
  if errors:
125
152
  return fail(errors)
126
153
 
@@ -1,21 +1,28 @@
1
1
  #!/usr/bin/env python3
2
- """File-based retrieval for the `absent` path.
2
+ """Hybrid retrieval file-first with optional package augmentation.
3
3
 
4
4
  Implements the shared `retrieve(types, keys, limit)` abstraction used
5
5
  by skills. Reads YAML under `agents/memory/<type>/` (curated, hand-
6
6
  reviewed) and JSONL under `agents/memory/intake/*.jsonl` (agent-written,
7
7
  append-only, supersede-chain aware).
8
8
 
9
- The returned shape is identical to the `present`-path adapter over the
10
- `@event4u/agent-memory` API, so skills stay backend-agnostic.
9
+ When the `@event4u/agent-memory` package is present (see
10
+ `scripts/memory_status.py`), callers can pass the result of
11
+ :func:`package_operational_provider` to route additional retrieval
12
+ through the package's semantic CLI. Repo entries always win on
13
+ conflict — see `_apply_conflict_rule`.
11
14
 
12
15
  Usage:
13
16
  python3 scripts/memory_lookup.py --types domain-invariants,ownership \\
14
17
  --key "app/Http/Controllers/Foo" --limit 5
15
18
  python3 scripts/memory_lookup.py --types incident-learnings --format json
19
+ python3 scripts/memory_lookup.py --types ownership --key billing --auto
16
20
 
17
- from scripts.memory_lookup import retrieve
18
- hits = retrieve(types=["ownership"], keys=["app/Http"], limit=3)
21
+ from scripts.memory_lookup import retrieve, package_operational_provider
22
+ hits = retrieve(
23
+ types=["ownership"], keys=["app/Http"], limit=3,
24
+ operational_provider=package_operational_provider(),
25
+ )
19
26
  """
20
27
 
21
28
  from __future__ import annotations
@@ -23,6 +30,8 @@ from __future__ import annotations
23
30
  import argparse
24
31
  import fnmatch
25
32
  import json
33
+ import os
34
+ import subprocess
26
35
  import sys
27
36
  from dataclasses import dataclass, asdict, field
28
37
  from pathlib import Path
@@ -229,6 +238,125 @@ def _apply_conflict_rule(
229
238
  return merged, shadows
230
239
 
231
240
 
241
+ # ---------------------------------------------------------------------------
242
+ # Package-backed operational provider (the `present` path)
243
+ # ---------------------------------------------------------------------------
244
+ #
245
+ # When `memory_status.status() == "present"` the consumer-facing contract
246
+ # says retrieval should route through `@event4u/agent-memory`. The package
247
+ # CLI is purely **semantic** (`memory retrieve <query> --type T …`); the
248
+ # shared `retrieve(types, keys, …)` API is **key-based**. The hybrid
249
+ # resolution agreed in `agents/contexts/agent-memory-contract.md` synthesises
250
+ # `keys` into a single natural-language query for the package call, while
251
+ # the file fallback continues to do glob/substring matching on the same
252
+ # keys. Both legs land in the same `Hit` shape so the conflict rule can
253
+ # merge them transparently.
254
+
255
+ _CLI_TIMEOUT_SECONDS = 5.0
256
+ _CLI_RETRIEVE_LIMIT_DEFAULT = 20
257
+
258
+
259
+ def _synthesize_query(keys: list[str]) -> str:
260
+ """Turn a list of retrieval keys into one natural-language query.
261
+
262
+ Keys are typically file paths (`app/Http/Controllers/Foo`), feature
263
+ names (`billing`), or short identifiers — joining them with spaces
264
+ gives the package's semantic search enough surface to score against
265
+ without inventing structure. Empty or whitespace-only keys are
266
+ dropped; if nothing remains the caller falls back to the file path.
267
+ """
268
+ cleaned = [k.strip() for k in keys if isinstance(k, str) and k.strip()]
269
+ return " ".join(cleaned)
270
+
271
+
272
+ def _cli_operational_provider(
273
+ types: list[str],
274
+ keys: list[str],
275
+ *,
276
+ cli_path: str = "memory",
277
+ timeout: float = _CLI_TIMEOUT_SECONDS,
278
+ limit: int = _CLI_RETRIEVE_LIMIT_DEFAULT,
279
+ ) -> Iterable[Hit]:
280
+ """Run `memory retrieve` and yield operational `Hit` objects.
281
+
282
+ Pino structured logs from the package go to stderr; stdout is a
283
+ clean v1 retrieval envelope. Any non-zero exit, timeout, or parse
284
+ failure degrades to "no operational hits" — `retrieve()` already
285
+ treats provider exceptions as a soft warning, so the caller still
286
+ gets the file-fallback result.
287
+ """
288
+ query = _synthesize_query(keys)
289
+ if not query:
290
+ return
291
+ cmd: list[str] = [cli_path, "retrieve", query, "--limit", str(limit)]
292
+ for t in types:
293
+ cmd.extend(["--type", t])
294
+ try:
295
+ out = subprocess.run(
296
+ cmd,
297
+ capture_output=True, text=True, timeout=timeout,
298
+ )
299
+ except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
300
+ return
301
+ if out.returncode != 0:
302
+ return
303
+ try:
304
+ envelope = json.loads(out.stdout)
305
+ except (ValueError, TypeError):
306
+ return
307
+ entries = envelope.get("entries") if isinstance(envelope, dict) else None
308
+ if not isinstance(entries, list):
309
+ return
310
+ for e in entries:
311
+ if not isinstance(e, dict):
312
+ continue
313
+ eid = e.get("id")
314
+ etype = e.get("type")
315
+ if not isinstance(eid, str) or not isinstance(etype, str):
316
+ continue
317
+ # The package returns `confidence` (0..1) per the v1 envelope;
318
+ # map it onto our internal `score` field so the conflict rule
319
+ # and ranking work uniformly across providers.
320
+ try:
321
+ score = float(e.get("confidence", 0.0))
322
+ except (TypeError, ValueError):
323
+ score = 0.0
324
+ body = e.get("body") if isinstance(e.get("body"), dict) else {}
325
+ yield Hit(
326
+ id=eid,
327
+ type=etype,
328
+ source="operational",
329
+ path=f"agent-memory:{eid}",
330
+ score=score,
331
+ entry=body,
332
+ )
333
+
334
+
335
+ def package_operational_provider() -> Optional[OperationalProvider]:
336
+ """Return a CLI-backed provider when the package is `present`, else None.
337
+
338
+ Callers who want automatic backend routing pass the result directly
339
+ to :func:`retrieve` — `None` is a safe value that yields file-only
340
+ retrieval, so this is the recommended one-liner for skills:
341
+
342
+ retrieve(types, keys, operational_provider=package_operational_provider())
343
+
344
+ The status probe is bounded (≤ 2s, cached per process) — see
345
+ `scripts/memory_status.py`. We import lazily so pure file-fallback
346
+ callers never pay for the probe.
347
+ """
348
+ # Late import: keeps `memory_lookup` importable even when
349
+ # `memory_status` is missing in stripped consumer installs.
350
+ try:
351
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
352
+ import memory_status # type: ignore[import-not-found]
353
+ except ImportError:
354
+ return None
355
+ if memory_status.status().status != "present":
356
+ return None
357
+ return _cli_operational_provider
358
+
359
+
232
360
  def retrieve(
233
361
  types: list[str],
234
362
  keys: list[str],
@@ -402,16 +530,24 @@ def main() -> int:
402
530
  ap.add_argument("--with-shadows", action="store_true",
403
531
  help="Include shadowed-operational entries in the output "
404
532
  "(no-op until an operational backend is wired)")
533
+ ap.add_argument("--auto", action="store_true",
534
+ help="Auto-route to the @event4u/agent-memory package "
535
+ "when memory_status.status() == 'present'; "
536
+ "falls through to file-only retrieval otherwise")
405
537
  args = ap.parse_args()
406
538
  types = [t.strip() for t in args.types.split(",") if t.strip()]
407
539
  if not types:
408
540
  print("error: --types is required", file=sys.stderr)
409
541
  return 2
542
+ op_provider = package_operational_provider() if args.auto else None
410
543
  if args.envelope == "v1":
411
- envelope = retrieve_v1(types, args.key, args.limit)
544
+ envelope = retrieve_v1(types, args.key, args.limit,
545
+ operational_provider=op_provider)
412
546
  print(json.dumps(envelope, indent=2, default=str))
413
547
  return 0
414
- result = retrieve(types, args.key, args.limit, with_shadows=args.with_shadows)
548
+ result = retrieve(types, args.key, args.limit,
549
+ operational_provider=op_provider,
550
+ with_shadows=args.with_shadows)
415
551
  if args.with_shadows:
416
552
  assert isinstance(result, RetrievalResult)
417
553
  hits, shadows = result.hits, result.shadows
@@ -35,7 +35,7 @@ from typing import Literal
35
35
 
36
36
  Status = Literal["absent", "misconfigured", "present"]
37
37
 
38
- _CLI_CANDIDATES = ("agent-memory", "agentmem")
38
+ _CLI_CANDIDATES = ("memory", "agent-memory", "agentmem")
39
39
  _HEALTH_TIMEOUT_SECONDS = 2.0
40
40
  _CACHE_ENV = "AGENT_MEMORY_STATUS"
41
41
  _CACHE_FILE = Path(".agent-memory") / "status.cache"
@@ -54,6 +54,11 @@ class Result:
54
54
  reason: str # short explanation
55
55
  elapsed_ms: int # time spent probing (0 if cached)
56
56
  cli_path: str = "" # resolved CLI path, if any
57
+ # Populated only when status == "present" — sourced from the
58
+ # `health` CLI envelope so the v1 health() reports real package
59
+ # capabilities instead of file-fallback placeholders.
60
+ backend_version: str = ""
61
+ features: tuple = ()
57
62
 
58
63
 
59
64
  def _find_cli() -> str:
@@ -64,8 +69,45 @@ def _find_cli() -> str:
64
69
  return ""
65
70
 
66
71
 
67
- def _probe_health(cli_path: str) -> tuple[bool, str]:
68
- """Returns (healthy, reason)."""
72
+ def _parse_health_envelope(stdout: str) -> dict | None:
73
+ """Extract the v1 health envelope from `memory health` stdout.
74
+
75
+ The package emits a single JSON object on stdout (pino structured
76
+ logs go to stderr). We tolerate older builds that may have leaked
77
+ log lines into stdout by scanning for the first top-level object
78
+ that carries ``contract_version``.
79
+ """
80
+ text = (stdout or "").strip()
81
+ if not text:
82
+ return None
83
+ try:
84
+ obj = json.loads(text)
85
+ except ValueError:
86
+ obj = None
87
+ if isinstance(obj, dict) and obj.get("contract_version"):
88
+ return obj
89
+ # Fallback: line-by-line scan for an envelope-shaped object — covers
90
+ # the case where structured logs accidentally share stdout.
91
+ for line in text.splitlines():
92
+ line = line.strip()
93
+ if not line.startswith("{"):
94
+ continue
95
+ try:
96
+ cand = json.loads(line)
97
+ except ValueError:
98
+ continue
99
+ if isinstance(cand, dict) and cand.get("contract_version"):
100
+ return cand
101
+ return None
102
+
103
+
104
+ def _probe_health(cli_path: str) -> tuple[bool, str, dict | None]:
105
+ """Returns (healthy, reason, envelope).
106
+
107
+ On success ``envelope`` is the parsed v1 health envelope (may still
108
+ be ``None`` for very old CLIs that don't emit one). On failure it
109
+ is always ``None``.
110
+ """
69
111
  try:
70
112
  out = subprocess.run(
71
113
  [cli_path, "health"],
@@ -73,15 +115,16 @@ def _probe_health(cli_path: str) -> tuple[bool, str]:
73
115
  timeout=_HEALTH_TIMEOUT_SECONDS,
74
116
  )
75
117
  except subprocess.TimeoutExpired:
76
- return False, f"health() timed out after {_HEALTH_TIMEOUT_SECONDS}s"
118
+ return False, f"health() timed out after {_HEALTH_TIMEOUT_SECONDS}s", None
77
119
  except FileNotFoundError:
78
- return False, "CLI vanished between which() and invoke"
120
+ return False, "CLI vanished between which() and invoke", None
79
121
  if out.returncode != 0:
80
122
  # First line of combined output, capped, for the reason field.
81
123
  msg = (out.stderr or out.stdout or "exit != 0").strip().splitlines()
82
124
  head = msg[0][:120] if msg else "exit != 0"
83
- return False, f"health() returned {out.returncode}: {head}"
84
- return True, "ok"
125
+ return False, f"health() returned {out.returncode}: {head}", None
126
+ envelope = _parse_health_envelope(out.stdout)
127
+ return True, "ok", envelope
85
128
 
86
129
 
87
130
  def _read_cache() -> Result | None:
@@ -131,10 +174,23 @@ def status(refresh: bool = False) -> Result:
131
174
  result = Result("absent", "file",
132
175
  "agent-memory CLI not on PATH", 0)
133
176
  else:
134
- healthy, reason = _probe_health(cli)
177
+ healthy, reason, envelope = _probe_health(cli)
135
178
  elapsed = int((time.monotonic() - t0) * 1000)
136
179
  if healthy:
137
- result = Result("present", "package", reason, elapsed, cli)
180
+ backend_version = ""
181
+ features: tuple = ()
182
+ if isinstance(envelope, dict):
183
+ bv = envelope.get("backend_version")
184
+ if isinstance(bv, str):
185
+ backend_version = bv
186
+ feats = envelope.get("features")
187
+ if isinstance(feats, list) and all(
188
+ isinstance(f, str) for f in feats
189
+ ):
190
+ features = tuple(feats)
191
+ result = Result("present", "package", reason, elapsed, cli,
192
+ backend_version=backend_version,
193
+ features=features)
138
194
  else:
139
195
  result = Result("misconfigured", "file", reason, elapsed, cli)
140
196
  _write_cache(result)
@@ -148,6 +204,11 @@ def health(refresh: bool = False) -> dict:
148
204
  Maps the three-state :func:`status` result onto the contract's
149
205
  ``ok | degraded | error`` so consumers can read
150
206
  ``contract_version`` without caring about the file-vs-package split.
207
+
208
+ When the package backs the call (``status == "present"``), the
209
+ envelope reports the package's own ``backend_version`` and
210
+ ``features`` so consumers can feature-detect against real
211
+ capabilities. Otherwise the file-fallback markers are returned.
151
212
  """
152
213
  r = status(refresh=refresh)
153
214
  envelope_status = {
@@ -155,11 +216,12 @@ def health(refresh: bool = False) -> dict:
155
216
  "misconfigured": "degraded",
156
217
  "absent": "ok",
157
218
  }[r.status]
158
- # When the package is present, report its version from `health()`
159
- # output; until we parse that, keep the file-fallback marker so the
160
- # envelope never lies about what backed the response.
161
- backend_version = _FILE_BACKEND_VERSION
162
- features = list(_FILE_BACKEND_FEATURES)
219
+ if r.status == "present" and (r.backend_version or r.features):
220
+ backend_version = r.backend_version or _FILE_BACKEND_VERSION
221
+ features = list(r.features) if r.features else list(_FILE_BACKEND_FEATURES)
222
+ else:
223
+ backend_version = _FILE_BACKEND_VERSION
224
+ features = list(_FILE_BACKEND_FEATURES)
163
225
  return {
164
226
  "contract_version": CONTRACT_VERSION,
165
227
  "status": envelope_status,
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env python3
2
+ """One-shot migration: inject the `suggestion:` frontmatter block into every
3
+ command under `.agent-src.uncompressed/commands/`.
4
+
5
+ Source-of-truth table: `agents/contexts/command-suggestion-eligibility.md`
6
+ (locked at end of road-to-context-aware-command-suggestion Phase 1).
7
+
8
+ Idempotent: if `suggestion:` is already present for a command, the file is
9
+ left untouched. Re-runnable.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import re
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ ROOT = Path(__file__).resolve().parent.parent
18
+ COMMANDS_DIR = ROOT / ".agent-src.uncompressed" / "commands"
19
+
20
+ INELIGIBLE: dict[str, str] = {
21
+ "agent-handoff": "Explicit fresh-chat handoff — must be deliberate, never inferred from prose.",
22
+ "agent-status": "Pure status display; no natural-language trigger distinct from idle small-talk.",
23
+ "agents-cleanup": "Consumes prior audit output; only meaningful right after /agents-audit.",
24
+ "agents-prepare": "One-shot project scaffolding; only run during initial setup.",
25
+ "chat-history": "Status display only; no NL trigger distinct from 'show status'.",
26
+ "chat-history-clear": "Destructive log wipe — must be deliberate.",
27
+ "chat-history-resume": "Explicit resume mechanic with foreign/returning state machine.",
28
+ "compress": "Package-internal tooling; only the event4u/agent-config repo runs this.",
29
+ "copilot-agents-init": "Project init — only deliberately during onboarding.",
30
+ "copilot-agents-optimize": "Maintenance refactor; only when the maintainer chooses to run it.",
31
+ "do-and-judge": "Subagent orchestration — overlaps /work and judge skills; keep explicit.",
32
+ "do-in-steps": "Subagent orchestration — overlaps /work and /roadmap-execute; keep explicit.",
33
+ "fix-portability": "Package-internal — only the event4u/agent-config repo runs this.",
34
+ "fix-references": "Package-internal — only the event4u/agent-config repo runs this.",
35
+ "judge": "Sibling of /review-changes — eligibility routed there; keep this explicit.",
36
+ "memory-full": "Description states 'never auto-triggered' — opt-in deep-load only.",
37
+ "memory-promote": "Curation pipeline — overlaps /memory-add; keep explicit.",
38
+ "mode": "Role-mode switch is a deliberate context change.",
39
+ "onboard": "Gated by the onboarding-gate rule already; never inferred from prose.",
40
+ "optimize-augmentignore": "Niche maintenance tool with no recurring NL trigger.",
41
+ "optimize-rtk-filters": "Niche maintenance tool with no recurring NL trigger.",
42
+ "package-reset": "Package-internal destructive reset.",
43
+ "package-test": "Package-internal — only the event4u/agent-config repo runs this.",
44
+ "propose-memory": "Programmatic intake fallback — overlaps /memory-add; keep explicit.",
45
+ "set-cost-profile": "Settings mutation — must be deliberate.",
46
+ "sync-agent-settings": "Settings sync — must be deliberate.",
47
+ "sync-gitignore": "Settings sync — must be deliberate.",
48
+ }
49
+
50
+ # (trigger_description, trigger_context)
51
+ ELIGIBLE: dict[str, tuple[str, str]] = {
52
+ "agents-audit": ("audit my agent docs, check the state of the agents/ directory", "stale files under agents/ or recent edits to .augment/ without doc updates"),
53
+ "analyze-reference-repo": ("look at how X does this, compare with that other repo, study this competitor's approach", "external repo URL or path mentioned in the prompt"),
54
+ "bug-fix": ("fix this bug, patch the issue, resolve this error", "branch name matches fix/* or bug/*"),
55
+ "bug-investigate": ("why is this broken, investigate this error, trace the root cause", "Sentry URL, Jira bug ticket key, or stack trace pasted in the prompt"),
56
+ "commit": ("commit my changes, save this to git, create commits for these changes", "git status shows uncommitted changes"),
57
+ "commit-in-chunks": ("commit everything autonomously, split and commit without confirmation", "autonomous mode active and uncommitted changes present"),
58
+ "context-create": ("document this part of the codebase, create a context doc for X", "working in a module without an agents/contexts/ doc"),
59
+ "context-refactor": ("update the context doc, refresh this context document", "existing agents/contexts/*.md referenced in the prompt"),
60
+ "create-pr": ("open a PR, create a pull request, make a PR for this branch", "branch is ahead of base and not yet on a PR"),
61
+ "create-pr-description": ("write a PR description, draft the PR text", "PR exists or branch ready for review without description"),
62
+ "e2e-heal": ("fix the failing E2E tests, playwright tests are red", "failing test output from tests/e2e/"),
63
+ "e2e-plan": ("plan E2E tests for this feature, what should we cover in playwright", "new feature or page added without tests/e2e/ coverage"),
64
+ "estimate-ticket": ("how big is this ticket, estimate PROJ-123, should we split this", "ticket key matching [A-Z]+-[0-9]+ in the prompt and no plan yet"),
65
+ "feature-dev": ("build this feature end-to-end, run the full feature workflow", "long-form feature description spanning multiple components"),
66
+ "feature-explore": ("brainstorm this idea, explore this feature concept", "open-ended feature idea without acceptance criteria"),
67
+ "feature-plan": ("plan this feature, create a feature spec for X", "feature idea referenced and no plan doc exists"),
68
+ "feature-refactor": ("update the feature plan, refine the feature spec", "existing agents/features/*.md referenced in the prompt"),
69
+ "feature-roadmap": ("turn this feature into a roadmap, generate the implementation roadmap", "existing feature plan without linked roadmap"),
70
+ "fix-ci": ("CI is failing, fix the GitHub Actions errors, the pipeline is red", "open PR with failing checks"),
71
+ "fix-pr-bot-comments": ("address the Copilot/Greptile comments, fix the bot review feedback", "open PR with bot review comments unresolved"),
72
+ "fix-pr-comments": ("fix all PR review comments, resolve the review feedback", "open PR with unresolved comments (bot + human)"),
73
+ "fix-pr-developer-comments": ("fix the human reviewer comments, address the developer feedback", "open PR with unresolved human-reviewer comments"),
74
+ "fix-seeder": ("the seeder is broken, foreign key errors in seeders", "seeder error output or recent edits in database/seeders/"),
75
+ "implement-ticket": ("implement this ticket, setze ticket X um, build PROJ-123", "ticket key matching [A-Z]+-[0-9]+ in branch name or prompt"),
76
+ "jira-ticket": ("implement the ticket on this branch, work on the Jira ticket from the branch", "branch name matching feat/PROJ-123-* or similar"),
77
+ "memory-add": ("remember this for later, add this to engineering memory, capture this learning", "post-incident or post-decision conversation"),
78
+ "module-create": ("create a new module, scaffold a module for X", "prompt mentions a new domain area without an existing module"),
79
+ "module-explore": ("show me the X module, load the module context", "existing Modules/<Name>/ referenced in the prompt"),
80
+ "optimize-agents": ("audit agent infrastructure, tune the agent setup", "maintainer working on .augment/ files"),
81
+ "optimize-skills": ("audit my skills, find duplicate skills", "maintainer working on .augment/skills/ files"),
82
+ "override-create": ("override this skill for the project, customize this rule locally", "prompt names a shared skill/rule needing project-specific behavior"),
83
+ "override-manage": ("review my overrides, update the project overrides", "existing entries under agents/overrides/"),
84
+ "prepare-for-review": ("get this branch ready for review, rebase and prep for PR", "branch behind base or part of a PR chain"),
85
+ "project-analyze": ("analyze the project structure, do a full project audit", "new project or after a major refactor"),
86
+ "project-health": ("check project health, what's the state of my docs and modules", "routine health check, no destructive intent"),
87
+ "quality-fix": ("fix the quality errors, run PHPStan and fix issues, fix code style", "PHPStan/Rector/ECS output in recent tool results"),
88
+ "refine-ticket": ("refine PROJ-123, tighten the acceptance criteria, is this ticket clear", "ticket key in prompt with vague acceptance criteria"),
89
+ "review-changes": ("self-review my changes, judge this diff before PR", "uncommitted or staged changes pre-PR"),
90
+ "review-routing": ("who should review this, suggest reviewers for this PR", "PR open without assigned reviewers"),
91
+ "roadmap-create": ("create a roadmap for X, plan this work as a roadmap", "multi-phase work without an existing agents/roadmaps/*.md"),
92
+ "roadmap-execute": ("execute the roadmap, work through the roadmap step by step", "existing agents/roadmaps/*.md referenced in the prompt"),
93
+ "rule-compliance-audit": ("audit my rules, check rule trigger quality", "maintainer working on .augment/rules/ files"),
94
+ "tests-create": ("write tests for these changes, add tests for this branch", "code changes on the branch without matching test changes"),
95
+ "tests-execute": ("run the tests, execute the test suite", "code changes pending verification"),
96
+ "threat-model": ("threat model this change, what could go wrong security-wise", "changes touching auth, webhooks, uploads, secrets, or public endpoints"),
97
+ "update-form-request-messages": ("sync the form request messages, update the validation messages", "edits to app/Http/Requests/*.php referencing rules without messages"),
98
+ "upstream-contribute": ("contribute this back to agent-config, upstream this learning", "project-local skill/rule that fits the shared package"),
99
+ "work": ("build this, implement this, drive this end-to-end", "free-form prompt without a ticket key"),
100
+ }
101
+
102
+ FRONTMATTER_RE = re.compile(r"^(---\n)(.*?)(\n---\n)", re.DOTALL)
103
+
104
+
105
+ def build_block(name: str) -> str:
106
+ if name in INELIGIBLE:
107
+ rationale = INELIGIBLE[name].replace('"', '\\"')
108
+ return f'suggestion:\n eligible: false\n rationale: "{rationale}"'
109
+ if name in ELIGIBLE:
110
+ td, tc = ELIGIBLE[name]
111
+ td_e = td.replace('"', '\\"')
112
+ tc_e = tc.replace('"', '\\"')
113
+ return (f'suggestion:\n eligible: true\n'
114
+ f' trigger_description: "{td_e}"\n'
115
+ f' trigger_context: "{tc_e}"')
116
+ raise SystemExit(f"command not classified: {name}")
117
+
118
+
119
+ def migrate_one(path: Path) -> str:
120
+ name = path.stem
121
+ text = path.read_text(encoding="utf-8")
122
+ if "\nsuggestion:" in text.split("\n---\n", 1)[0]:
123
+ return "skip"
124
+ m = FRONTMATTER_RE.search(text)
125
+ if not m:
126
+ return "no-frontmatter"
127
+ body = m.group(2)
128
+ block = build_block(name)
129
+ new_body = body.rstrip() + "\n" + block
130
+ new_text = text[: m.start()] + m.group(1) + new_body + m.group(3) + text[m.end():]
131
+ path.write_text(new_text, encoding="utf-8")
132
+ return "ok"
133
+
134
+
135
+ def main() -> int:
136
+ files = sorted(COMMANDS_DIR.glob("*.md"))
137
+ counts = {"ok": 0, "skip": 0, "no-frontmatter": 0}
138
+ for f in files:
139
+ status = migrate_one(f)
140
+ counts[status] += 1
141
+ print(f"migrated {counts['ok']}, skipped {counts['skip']}, "
142
+ f"no-frontmatter {counts['no-frontmatter']}, total {len(files)}")
143
+ expected = len(INELIGIBLE) + len(ELIGIBLE)
144
+ if len(files) != expected:
145
+ print(f"WARNING: file count {len(files)} != table count {expected}", file=sys.stderr)
146
+ return 1
147
+ return 0
148
+
149
+
150
+ if __name__ == "__main__":
151
+ sys.exit(main())
@@ -33,6 +33,22 @@ trap 'rm -f "$LOG"' EXIT
33
33
  bash "$INSTALLER" --quiet >"$LOG" 2>&1
34
34
  CODE=$?
35
35
  if [[ $CODE -eq 0 ]]; then
36
+ # Optional companion: @event4u/agent-memory. Suggest it once, only if
37
+ # the consumer hasn't already installed it (locally or on PATH). The
38
+ # hint is purely informational; agent-config falls back to file-based
39
+ # memory when the backend is absent.
40
+ if ! command -v memory >/dev/null 2>&1 \
41
+ && ! command -v agent-memory >/dev/null 2>&1 \
42
+ && [[ ! -d "$SCRIPT_DIR/../../@event4u/agent-memory" ]]; then
43
+ cat >&2 <<'HINT'
44
+ 💡 agent-config tip: install @event4u/agent-memory for persistent agent
45
+ learnings across sessions (optional, dev-only):
46
+
47
+ npm install --save-dev @event4u/agent-memory
48
+
49
+ Skip if you don't need it — agent-config falls back to file-based memory.
50
+ HINT
51
+ fi
36
52
  exit 0
37
53
  fi
38
54
 
@@ -27,6 +27,47 @@
27
27
  "type": "string",
28
28
  "pattern": "^[a-z][a-z0-9-]*$"
29
29
  }
30
+ },
31
+ "suggestion": {
32
+ "type": "object",
33
+ "additionalProperties": false,
34
+ "required": ["eligible"],
35
+ "description": "Context-aware command-suggestion metadata. The suggestion layer is read-only and never auto-executes; see road-to-context-aware-command-suggestion.",
36
+ "properties": {
37
+ "eligible": {
38
+ "type": "boolean",
39
+ "description": "true → suggestion layer may surface this command; false → never suggest (still works when typed explicitly)."
40
+ },
41
+ "rationale": {
42
+ "type": "string",
43
+ "minLength": 0,
44
+ "maxLength": 240,
45
+ "description": "Why the command is ineligible. Required when eligible: false (enforced by linter)."
46
+ },
47
+ "trigger_description": {
48
+ "type": "string",
49
+ "minLength": 0,
50
+ "maxLength": 240,
51
+ "description": "Natural-language pattern trigger. Required when eligible: true (enforced by linter)."
52
+ },
53
+ "trigger_context": {
54
+ "type": "string",
55
+ "minLength": 0,
56
+ "maxLength": 240,
57
+ "description": "Concrete signal trigger (branch name, file pattern, recent tool output). Required when eligible: true (enforced by linter)."
58
+ },
59
+ "confidence_floor": {
60
+ "type": "number",
61
+ "minimum": 0,
62
+ "description": "Optional per-command override of the global confidence floor (0.0–1.0). Defaults to settings."
63
+ },
64
+ "cooldown": {
65
+ "type": "string",
66
+ "minLength": 0,
67
+ "maxLength": 16,
68
+ "description": "Optional per-command override of the global cooldown duration (e.g. '10m'). Defaults to settings."
69
+ }
70
+ }
30
71
  }
31
72
  }
32
73
  }