agent-devkit 0.2.0 → 0.3.1

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 (177) hide show
  1. package/README.md +66 -13
  2. package/bin/agent.mjs +133 -7
  3. package/package.json +1 -1
  4. package/runtime/README.md +205 -13
  5. package/runtime/agent +31 -5
  6. package/runtime/agents/README.md +18 -0
  7. package/runtime/agents/contribution-reviewer/AGENTS.md +8 -0
  8. package/runtime/agents/contribution-reviewer/README.md +8 -0
  9. package/runtime/agents/contribution-reviewer/agent.yaml +40 -0
  10. package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/capability.yaml +27 -0
  11. package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/decision-rules.md +5 -0
  12. package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/workflow.md +6 -0
  13. package/runtime/agents/contribution-reviewer/capabilities/review-contribution/capability.yaml +25 -0
  14. package/runtime/agents/contribution-reviewer/capabilities/review-contribution/decision-rules.md +5 -0
  15. package/runtime/agents/contribution-reviewer/capabilities/review-contribution/workflow.md +5 -0
  16. package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/capability.yaml +26 -0
  17. package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/decision-rules.md +5 -0
  18. package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/workflow.md +6 -0
  19. package/runtime/agents/contribution-reviewer/infra/README.md +6 -0
  20. package/runtime/agents/contribution-reviewer/knowledge/context.md +8 -0
  21. package/runtime/agents/contribution-reviewer/knowledge/system.md +8 -0
  22. package/runtime/agents/contribution-reviewer/templates/README.md +3 -0
  23. package/runtime/agents/knowledge-author/AGENTS.md +7 -0
  24. package/runtime/agents/knowledge-author/README.md +7 -0
  25. package/runtime/agents/knowledge-author/agent.yaml +37 -0
  26. package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/capability.yaml +30 -0
  27. package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/decision-rules.md +6 -0
  28. package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/workflow.md +7 -0
  29. package/runtime/agents/knowledge-author/infra/.gitkeep +1 -0
  30. package/runtime/agents/knowledge-author/knowledge/context.md +4 -0
  31. package/runtime/agents/knowledge-author/knowledge/system.md +4 -0
  32. package/runtime/agents/knowledge-author/templates/.gitkeep +1 -0
  33. package/runtime/agents/knowledge-curator/AGENTS.md +7 -0
  34. package/runtime/agents/knowledge-curator/README.md +6 -0
  35. package/runtime/agents/knowledge-curator/agent.yaml +37 -0
  36. package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/capability.yaml +29 -0
  37. package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/decision-rules.md +6 -0
  38. package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/workflow.md +7 -0
  39. package/runtime/agents/knowledge-curator/infra/.gitkeep +1 -0
  40. package/runtime/agents/knowledge-curator/knowledge/context.md +4 -0
  41. package/runtime/agents/knowledge-curator/knowledge/system.md +4 -0
  42. package/runtime/agents/knowledge-curator/templates/.gitkeep +1 -0
  43. package/runtime/agents/knowledge-infra-builder/AGENTS.md +8 -0
  44. package/runtime/agents/knowledge-infra-builder/README.md +8 -0
  45. package/runtime/agents/knowledge-infra-builder/agent.yaml +38 -0
  46. package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/capability.yaml +30 -0
  47. package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/decision-rules.md +6 -0
  48. package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/workflow.md +7 -0
  49. package/runtime/agents/knowledge-infra-builder/infra/.gitkeep +1 -0
  50. package/runtime/agents/knowledge-infra-builder/knowledge/context.md +4 -0
  51. package/runtime/agents/knowledge-infra-builder/knowledge/system.md +4 -0
  52. package/runtime/agents/knowledge-infra-builder/templates/.gitkeep +1 -0
  53. package/runtime/agents/knowledge-owner/AGENTS.md +7 -0
  54. package/runtime/agents/knowledge-owner/README.md +6 -0
  55. package/runtime/agents/knowledge-owner/agent.yaml +37 -0
  56. package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/capability.yaml +28 -0
  57. package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/decision-rules.md +6 -0
  58. package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/workflow.md +7 -0
  59. package/runtime/agents/knowledge-owner/infra/.gitkeep +1 -0
  60. package/runtime/agents/knowledge-owner/knowledge/context.md +4 -0
  61. package/runtime/agents/knowledge-owner/knowledge/system.md +4 -0
  62. package/runtime/agents/knowledge-owner/templates/.gitkeep +1 -0
  63. package/runtime/agents/knowledge-reviewer/AGENTS.md +7 -0
  64. package/runtime/agents/knowledge-reviewer/README.md +7 -0
  65. package/runtime/agents/knowledge-reviewer/agent.yaml +36 -0
  66. package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/capability.yaml +26 -0
  67. package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/decision-rules.md +6 -0
  68. package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/workflow.md +7 -0
  69. package/runtime/agents/knowledge-reviewer/infra/.gitkeep +1 -0
  70. package/runtime/agents/knowledge-reviewer/knowledge/context.md +4 -0
  71. package/runtime/agents/knowledge-reviewer/knowledge/system.md +4 -0
  72. package/runtime/agents/knowledge-reviewer/templates/.gitkeep +1 -0
  73. package/runtime/agents/local-memory-manager/AGENTS.md +5 -0
  74. package/runtime/agents/local-memory-manager/README.md +7 -0
  75. package/runtime/agents/local-memory-manager/agent.yaml +38 -0
  76. package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/capability.yaml +19 -0
  77. package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/decision-rules.md +5 -0
  78. package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/workflow.md +6 -0
  79. package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/capability.yaml +19 -0
  80. package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/decision-rules.md +5 -0
  81. package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/workflow.md +5 -0
  82. package/runtime/agents/local-memory-manager/infra/.gitkeep +1 -0
  83. package/runtime/agents/local-memory-manager/knowledge/context.md +4 -0
  84. package/runtime/agents/local-memory-manager/knowledge/system.md +4 -0
  85. package/runtime/agents/local-memory-manager/templates/.gitkeep +1 -0
  86. package/runtime/agents/memory-sync-manager/AGENTS.md +7 -0
  87. package/runtime/agents/memory-sync-manager/README.md +7 -0
  88. package/runtime/agents/memory-sync-manager/agent.yaml +37 -0
  89. package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/capability.yaml +29 -0
  90. package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/decision-rules.md +6 -0
  91. package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/workflow.md +7 -0
  92. package/runtime/agents/memory-sync-manager/infra/.gitkeep +1 -0
  93. package/runtime/agents/memory-sync-manager/knowledge/context.md +4 -0
  94. package/runtime/agents/memory-sync-manager/knowledge/system.md +4 -0
  95. package/runtime/agents/memory-sync-manager/templates/.gitkeep +1 -0
  96. package/runtime/agents/shared-memory-curator/AGENTS.md +5 -0
  97. package/runtime/agents/shared-memory-curator/README.md +6 -0
  98. package/runtime/agents/shared-memory-curator/agent.yaml +38 -0
  99. package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/capability.yaml +19 -0
  100. package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/decision-rules.md +5 -0
  101. package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/workflow.md +5 -0
  102. package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/capability.yaml +19 -0
  103. package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/decision-rules.md +5 -0
  104. package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/workflow.md +5 -0
  105. package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/capability.yaml +19 -0
  106. package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/decision-rules.md +5 -0
  107. package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/workflow.md +5 -0
  108. package/runtime/agents/shared-memory-curator/infra/.gitkeep +1 -0
  109. package/runtime/agents/shared-memory-curator/knowledge/context.md +5 -0
  110. package/runtime/agents/shared-memory-curator/knowledge/system.md +4 -0
  111. package/runtime/agents/shared-memory-curator/templates/.gitkeep +1 -0
  112. package/runtime/cli/README.md +47 -8
  113. package/runtime/cli/aikit/__init__.py +1 -1
  114. package/runtime/cli/aikit/agent_registry.py +4 -2
  115. package/runtime/cli/aikit/agentic_commands.py +158 -0
  116. package/runtime/cli/aikit/app_home.py +2 -0
  117. package/runtime/cli/aikit/audit.py +16 -6
  118. package/runtime/cli/aikit/catalog.py +278 -8
  119. package/runtime/cli/aikit/cli_dispatch.py +489 -13
  120. package/runtime/cli/aikit/cli_parser.py +146 -8
  121. package/runtime/cli/aikit/contribution.py +132 -2
  122. package/runtime/cli/aikit/doctor_runtime.py +85 -0
  123. package/runtime/cli/aikit/embedded_mini_brain.py +351 -0
  124. package/runtime/cli/aikit/eval.py +356 -10
  125. package/runtime/cli/aikit/human_output.py +310 -4
  126. package/runtime/cli/aikit/interactive_wizard.py +146 -0
  127. package/runtime/cli/aikit/knowledge_base.py +1067 -0
  128. package/runtime/cli/aikit/llm.py +40 -6
  129. package/runtime/cli/aikit/local_artifacts.py +444 -0
  130. package/runtime/cli/aikit/local_llm.py +176 -0
  131. package/runtime/cli/aikit/local_llm_operator.py +15 -5
  132. package/runtime/cli/aikit/main.py +15 -0
  133. package/runtime/cli/aikit/mcp_manifest.py +798 -0
  134. package/runtime/cli/aikit/mcp_tools.py +643 -5
  135. package/runtime/cli/aikit/memory.py +405 -0
  136. package/runtime/cli/aikit/mini_brain.py +56 -25
  137. package/runtime/cli/aikit/model_router.py +42 -9
  138. package/runtime/cli/aikit/natural_prompt_runtime.py +194 -2
  139. package/runtime/cli/aikit/ollama.py +64 -15
  140. package/runtime/cli/aikit/onboarding.py +551 -0
  141. package/runtime/cli/aikit/output.py +67 -0
  142. package/runtime/cli/aikit/prompt_injection.py +12 -1
  143. package/runtime/cli/aikit/review_gate.py +14 -2
  144. package/runtime/cli/aikit/roadmap_cli.py +1 -1
  145. package/runtime/cli/aikit/secrets.py +3 -2
  146. package/runtime/cli/aikit/setup_wizard_payload.py +3 -0
  147. package/runtime/cli/aikit/shared_memory.py +415 -0
  148. package/runtime/cli/aikit/specialist_readiness.py +152 -0
  149. package/runtime/cli/aikit/tasks.py +104 -1
  150. package/runtime/cli/aikit/team.py +380 -0
  151. package/runtime/cli/aikit/toolchain.py +7 -2
  152. package/runtime/cli/aikit/workflows.py +115 -14
  153. package/runtime/models/qwen2.5-0.5b-instruct/manifest.json +30 -0
  154. package/runtime/providers/knowledge-github.yaml +40 -0
  155. package/runtime/providers/knowledge-google-drive.yaml +32 -0
  156. package/runtime/providers/knowledge-local.yaml +26 -0
  157. package/runtime/providers/knowledge-notion.yaml +32 -0
  158. package/runtime/providers/knowledge-obsidian.yaml +24 -0
  159. package/runtime/providers/knowledge-onedrive.yaml +36 -0
  160. package/runtime/providers/knowledge-s3.yaml +45 -0
  161. package/runtime/providers/knowledge-sharepoint.yaml +39 -0
  162. package/runtime/providers/knowledge-supabase.yaml +43 -0
  163. package/runtime/providers/knowledge-vector.yaml +39 -0
  164. package/runtime/requirements.txt +6 -0
  165. package/runtime/scripts/docker-cli-qa.sh +453 -0
  166. package/runtime/scripts/release-catalog-snapshot.json +55 -4
  167. package/runtime/scripts/release-gate.py +54 -13
  168. package/runtime/tooling/toolchain.yaml +92 -0
  169. package/runtime/vendor/skills/napkin/napkin.md +21 -7
  170. package/runtime/workflows/azure-card-analysis/README.md +3 -0
  171. package/runtime/workflows/azure-card-analysis/workflow.yaml +30 -0
  172. package/runtime/workflows/daily-pr-review/README.md +3 -0
  173. package/runtime/workflows/daily-pr-review/workflow.yaml +31 -0
  174. package/runtime/workflows/incident-analysis/README.md +3 -0
  175. package/runtime/workflows/incident-analysis/workflow.yaml +33 -0
  176. package/runtime/workflows/release-prep/README.md +3 -0
  177. package/runtime/workflows/release-prep/workflow.yaml +30 -0
@@ -2,24 +2,28 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import json
5
6
  import re
6
7
  from pathlib import Path
7
8
  from typing import Any
8
9
 
10
+ from cli.aikit.app_home import cache_home
9
11
  from cli.aikit.agent_registry import load_agent_registry
10
12
  from cli.aikit.errors import DevKitError
13
+ from cli.aikit.extensions import local_extensions_list
14
+ from cli.aikit.local_artifacts import local_agent_list, local_automation_list, script_list, skill_list
11
15
  from cli.aikit.providers import ProviderRegistryError, list_providers
12
16
  from cli.aikit.runtime_paths import ROOT
17
+ from cli.aikit.toolchain import list_toolchain
18
+ from cli.aikit.workflows import workflow_list
13
19
 
14
20
 
15
21
  CATALOG_SCHEMA_VERSION = "agent-devkit.catalog/v1"
16
22
 
17
23
 
18
- def catalog_list(root: Path | None = None, *, item_type: str | None = None) -> dict[str, Any]:
24
+ def catalog_list(root: Path | None = None, *, item_type: str | None = None, filters: dict[str, Any] | None = None) -> dict[str, Any]:
19
25
  root = root or ROOT
20
- items = catalog_items(root)
21
- if item_type:
22
- items = [item for item in items if item["type"] == item_type]
26
+ items = filtered_catalog_items(root, item_type=item_type, filters=filters)
23
27
  return {
24
28
  "kind": "catalog",
25
29
  "schema_version": CATALOG_SCHEMA_VERSION,
@@ -28,20 +32,19 @@ def catalog_list(root: Path | None = None, *, item_type: str | None = None) -> d
28
32
  "query": None,
29
33
  "type": item_type,
30
34
  "count": len(items),
35
+ "filters": public_filters(item_type=item_type, filters=filters),
31
36
  "items": items,
32
37
  }
33
38
 
34
39
 
35
- def catalog_search(query: str, root: Path | None = None, *, item_type: str | None = None) -> dict[str, Any]:
40
+ def catalog_search(query: str, root: Path | None = None, *, item_type: str | None = None, filters: dict[str, Any] | None = None) -> dict[str, Any]:
36
41
  root = root or ROOT
37
42
  query = (query or "").strip()
38
43
  if not query:
39
44
  raise DevKitError("catalog search requires a query")
40
45
  tokens = tokenize(query)
41
46
  scored: list[tuple[int, dict[str, Any]]] = []
42
- for item in catalog_items(root):
43
- if item_type and item["type"] != item_type:
44
- continue
47
+ for item in filtered_catalog_items(root, item_type=item_type, filters=filters):
45
48
  score = match_score(item, tokens)
46
49
  if score > 0:
47
50
  enriched = dict(item)
@@ -56,6 +59,7 @@ def catalog_search(query: str, root: Path | None = None, *, item_type: str | Non
56
59
  "query": query,
57
60
  "type": item_type,
58
61
  "count": len(scored),
62
+ "filters": public_filters(item_type=item_type, filters=filters),
59
63
  "items": [item for _score, item in scored],
60
64
  }
61
65
 
@@ -80,6 +84,25 @@ def catalog_show(item_id: str, root: Path | None = None, *, item_type: str | Non
80
84
  raise DevKitError(f"catalog item not found{suffix}: {item_id}")
81
85
 
82
86
 
87
+ def catalog_rebuild_index(root: Path | None = None) -> dict[str, Any]:
88
+ root = root or ROOT
89
+ items = catalog_items(root)
90
+ path = catalog_index_path()
91
+ path.parent.mkdir(parents=True, exist_ok=True)
92
+ payload = {
93
+ "schema_version": CATALOG_SCHEMA_VERSION,
94
+ "items": items,
95
+ }
96
+ path.write_text(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8")
97
+ return {
98
+ "kind": "catalog-index",
99
+ "schema_version": CATALOG_SCHEMA_VERSION,
100
+ "status": "rebuilt",
101
+ "path": str(path),
102
+ "count": len(items),
103
+ }
104
+
105
+
83
106
  def catalog_items(root: Path) -> list[dict[str, Any]]:
84
107
  registry = load_agent_registry(root)
85
108
  items: list[dict[str, Any]] = []
@@ -90,6 +113,33 @@ def catalog_items(root: Path) -> list[dict[str, Any]]:
90
113
  if isinstance(capability, dict):
91
114
  items.append(capability_item(capability))
92
115
  items.extend(provider_items(root))
116
+ items.extend(workflow_items())
117
+ items.extend(tool_items(root))
118
+ items.extend(skill_items(root))
119
+ items.extend(plugin_items(root))
120
+ items.extend(extension_items())
121
+ items.extend(local_artifact_items())
122
+ return items
123
+
124
+
125
+ def filtered_catalog_items(root: Path, *, item_type: str | None = None, filters: dict[str, Any] | None = None) -> list[dict[str, Any]]:
126
+ filters = {key: value for key, value in (filters or {}).items() if value not in {None, ""}}
127
+ items = catalog_items(root)
128
+ if item_type:
129
+ items = [
130
+ item
131
+ for item in items
132
+ if item["type"] == item_type or item.get("local_kind") == item_type
133
+ ]
134
+ for key, value in filters.items():
135
+ if key == "provider":
136
+ items = [item for item in items if value in (item.get("providers_required") or []) or item.get("provider") == value]
137
+ elif key == "status":
138
+ items = [item for item in items if item.get("status") == value]
139
+ elif key == "write_policy":
140
+ items = [item for item in items if item.get("write_policy") == value]
141
+ elif key == "readiness":
142
+ items = [item for item in items if (item.get("readiness") or {}).get("status") == value]
93
143
  return items
94
144
 
95
145
 
@@ -105,6 +155,7 @@ def agent_item(agent: dict[str, Any]) -> dict[str, Any]:
105
155
  "write_policy": agent.get("write_policy"),
106
156
  "write_policy_metadata": agent.get("write_policy_metadata"),
107
157
  "source_contract": None,
158
+ "provider": None,
108
159
  "providers_required": [],
109
160
  "runner": None,
110
161
  "agent_mode": agent.get("agent_mode") or {},
@@ -142,6 +193,7 @@ def capability_item(capability: dict[str, Any]) -> dict[str, Any]:
142
193
  "write_policy": capability.get("write_policy"),
143
194
  "write_policy_metadata": capability.get("write_policy_metadata"),
144
195
  "source_contract": capability.get("source_contract") or capability.get("source"),
196
+ "provider": provider,
145
197
  "providers_required": providers_required,
146
198
  "runner": capability.get("runner") or (capability.get("runtime") or {}).get("runner"),
147
199
  "agent_mode": None,
@@ -174,6 +226,7 @@ def provider_items(root: Path) -> list[dict[str, Any]]:
174
226
  "path": provider.get("path"),
175
227
  "write_policy": "read_only",
176
228
  "source_contract": None,
229
+ "provider": provider.get("id"),
177
230
  "providers_required": [],
178
231
  "runner": None,
179
232
  "agent_mode": None,
@@ -185,6 +238,223 @@ def provider_items(root: Path) -> list[dict[str, Any]]:
185
238
  return items
186
239
 
187
240
 
241
+ def workflow_items() -> list[dict[str, Any]]:
242
+ try:
243
+ payload = workflow_list()
244
+ except Exception:
245
+ return []
246
+ return [
247
+ {
248
+ "id": str(workflow.get("id") or ""),
249
+ "type": "workflow",
250
+ "description": workflow.get("description") or workflow.get("title") or "",
251
+ "status": "ok",
252
+ "version": None,
253
+ "path": workflow.get("path"),
254
+ "write_policy": workflow.get("write_policy"),
255
+ "source_contract": None,
256
+ "provider": None,
257
+ "providers_required": workflow.get("providers_required") or [],
258
+ "runner": "workflow",
259
+ "agent_mode": None,
260
+ "routing": {},
261
+ "prompt_examples": workflow.get("examples") or [],
262
+ "readiness": {"status": "ready"},
263
+ "next_step": f"agent workflow show {workflow.get('id')}",
264
+ }
265
+ for workflow in payload.get("items") or []
266
+ if isinstance(workflow, dict)
267
+ ]
268
+
269
+
270
+ def tool_items(root: Path) -> list[dict[str, Any]]:
271
+ try:
272
+ payload = list_toolchain(root)
273
+ except Exception:
274
+ return []
275
+ return [
276
+ {
277
+ "id": str(tool.get("id") or ""),
278
+ "type": "tool",
279
+ "description": tool.get("notes") or tool.get("label") or "",
280
+ "status": "required" if tool.get("required") else "optional",
281
+ "version": None,
282
+ "path": None,
283
+ "write_policy": "confirm",
284
+ "source_contract": None,
285
+ "provider": None,
286
+ "providers_required": [],
287
+ "runner": tool.get("command"),
288
+ "agent_mode": None,
289
+ "routing": {},
290
+ "prompt_examples": [],
291
+ "readiness": {"status": "requires-install-check", "command": tool.get("command")},
292
+ "next_step": f"agent toolchain doctor {tool.get('id')}",
293
+ }
294
+ for tool in payload.get("items") or []
295
+ if isinstance(tool, dict)
296
+ ]
297
+
298
+
299
+ def skill_items(root: Path) -> list[dict[str, Any]]:
300
+ catalog = parse_markdown_catalog(root / "vendor" / "skills" / "CATALOG.md", item_type="skill")
301
+ if catalog:
302
+ return catalog
303
+ return filesystem_items(root / "vendor" / "skills", item_type="skill", marker="SKILL.md")
304
+
305
+
306
+ def plugin_items(root: Path) -> list[dict[str, Any]]:
307
+ catalog = parse_markdown_catalog(root / "vendor" / "plugins" / "CATALOG.md", item_type="plugin")
308
+ if catalog:
309
+ return catalog
310
+ return filesystem_items(root / "vendor" / "plugins", item_type="plugin", marker="README.md")
311
+
312
+
313
+ def extension_items() -> list[dict[str, Any]]:
314
+ try:
315
+ payload = local_extensions_list()
316
+ except Exception:
317
+ return []
318
+ return [
319
+ {
320
+ "id": str(item.get("id") or ""),
321
+ "type": "extension",
322
+ "description": f"Local extension at {item.get('path')}",
323
+ "status": "enabled" if item.get("enabled") else "disabled",
324
+ "version": None,
325
+ "path": item.get("path"),
326
+ "write_policy": "local_config_write",
327
+ "source_contract": None,
328
+ "provider": None,
329
+ "providers_required": [],
330
+ "runner": None,
331
+ "agent_mode": None,
332
+ "routing": {},
333
+ "prompt_examples": [],
334
+ "readiness": {"status": "ready" if item.get("enabled") else "disabled"},
335
+ "next_step": f"agent local validate {item.get('id')}",
336
+ }
337
+ for item in payload.get("items") or []
338
+ if isinstance(item, dict)
339
+ ]
340
+
341
+
342
+ def local_artifact_items() -> list[dict[str, Any]]:
343
+ payloads = []
344
+ for factory, item_type, command in (
345
+ (skill_list, "skill", "agent skill show"),
346
+ (script_list, "script", "agent script run"),
347
+ (local_agent_list, "agent", "agent agents validate"),
348
+ (local_automation_list, "automation", "agent local automation show"),
349
+ ):
350
+ try:
351
+ payload = factory()
352
+ except Exception:
353
+ continue
354
+ for item in payload.get("items") or []:
355
+ if not isinstance(item, dict):
356
+ continue
357
+ payloads.append(
358
+ {
359
+ "id": str(item.get("id") or ""),
360
+ "type": item_type,
361
+ "local_kind": item.get("kind"),
362
+ "origin": "local",
363
+ "description": f"Local {item_type} at {item.get('path')}",
364
+ "status": "enabled" if item.get("enabled") else "disabled",
365
+ "version": None,
366
+ "path": item.get("path"),
367
+ "write_policy": "local_config_write",
368
+ "source_contract": None,
369
+ "provider": None,
370
+ "providers_required": [],
371
+ "runner": None,
372
+ "agent_mode": None,
373
+ "routing": {},
374
+ "prompt_examples": [],
375
+ "readiness": {"status": "ready" if item.get("enabled") else "disabled"},
376
+ "next_step": f"{command} {item.get('id')}",
377
+ }
378
+ )
379
+ return payloads
380
+
381
+
382
+ def parse_markdown_catalog(path: Path, *, item_type: str) -> list[dict[str, Any]]:
383
+ if not path.exists():
384
+ return []
385
+ items: list[dict[str, Any]] = []
386
+ for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
387
+ stripped = line.strip()
388
+ if not stripped.startswith("| `"):
389
+ continue
390
+ cells = [cell.strip() for cell in stripped.strip("|").split("|")]
391
+ if len(cells) < 3:
392
+ continue
393
+ item_id = cells[0].strip("` ")
394
+ description = cells[1]
395
+ path_cell = cells[-1].strip("` ")
396
+ items.append(
397
+ {
398
+ "id": item_id,
399
+ "type": item_type,
400
+ "description": description,
401
+ "status": "available",
402
+ "version": None,
403
+ "path": path_cell,
404
+ "write_policy": "read_only",
405
+ "source_contract": None,
406
+ "provider": None,
407
+ "providers_required": [],
408
+ "runner": None,
409
+ "agent_mode": None,
410
+ "routing": {},
411
+ "prompt_examples": [],
412
+ "readiness": {"status": "available"},
413
+ "next_step": f"Use {item_type} {item_id} when relevant.",
414
+ }
415
+ )
416
+ return items
417
+
418
+
419
+ def filesystem_items(root: Path, *, item_type: str, marker: str) -> list[dict[str, Any]]:
420
+ if not root.exists():
421
+ return []
422
+ items = []
423
+ for marker_path in sorted(root.glob(f"**/{marker}")):
424
+ item_root = marker_path.parent
425
+ items.append(
426
+ {
427
+ "id": item_root.name,
428
+ "type": item_type,
429
+ "description": "",
430
+ "status": "available",
431
+ "version": None,
432
+ "path": str(item_root.relative_to(ROOT)) if item_root.is_relative_to(ROOT) else str(item_root),
433
+ "write_policy": "read_only",
434
+ "source_contract": None,
435
+ "provider": None,
436
+ "providers_required": [],
437
+ "runner": None,
438
+ "agent_mode": None,
439
+ "routing": {},
440
+ "prompt_examples": [],
441
+ "readiness": {"status": "available"},
442
+ }
443
+ )
444
+ return items
445
+
446
+
447
+ def catalog_index_path() -> Path:
448
+ return cache_home() / "catalog-index.json"
449
+
450
+
451
+ def public_filters(*, item_type: str | None, filters: dict[str, Any] | None) -> dict[str, Any]:
452
+ payload = {key: value for key, value in (filters or {}).items() if value not in {None, ""}}
453
+ if item_type:
454
+ payload["type"] = item_type
455
+ return payload
456
+
457
+
188
458
  def tokenize(value: str) -> set[str]:
189
459
  tokens = {token for token in re.findall(r"[a-z0-9]+", value.lower()) if token}
190
460
  return expand_query_tokens(tokens)