agentera 0.0.0 → 3.0.0-dev.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 (256) hide show
  1. package/README.md +6 -45
  2. package/bundle/.agentera-npx-bundle.json +4 -0
  3. package/bundle/references/adapters/cursor.md +213 -0
  4. package/bundle/references/adapters/opencode.md +530 -0
  5. package/bundle/references/adapters/package-manifest-interface-model.yaml +337 -0
  6. package/bundle/references/adapters/package-registry.yaml +247 -0
  7. package/bundle/references/adapters/package-surface-characterization.md +48 -0
  8. package/bundle/references/adapters/runtime-adapter-characterization.md +79 -0
  9. package/bundle/references/adapters/runtime-adapter-interface-model.yaml +200 -0
  10. package/bundle/references/adapters/runtime-adapter-registry.yaml +548 -0
  11. package/bundle/references/adapters/runtime-feature-parity.md +189 -0
  12. package/bundle/references/analysis/benchmark.md +267 -0
  13. package/bundle/references/analysis/startup-measurement-contract.yaml +424 -0
  14. package/bundle/references/artifacts/artifact-registry-interface-model.yaml +288 -0
  15. package/bundle/references/cli/agent-ready-state-contract.yaml +950 -0
  16. package/bundle/references/cli/app-lifecycle-vocabulary.yaml +241 -0
  17. package/bundle/references/cli/audience-namespace-cli-migration.yaml +355 -0
  18. package/bundle/references/cli/bundle-skill-vocabulary.yaml +278 -0
  19. package/bundle/references/cli/capability-instruction-contract.yaml +123 -0
  20. package/bundle/references/cli/capability-tool-classification.yaml +53 -0
  21. package/bundle/references/cli/routing-execution-vocabulary.yaml +281 -0
  22. package/bundle/references/cli/update-channels.yaml +147 -0
  23. package/bundle/references/cli/vocabulary-index.yaml +160 -0
  24. package/bundle/references/cli/vocabulary.md +566 -0
  25. package/bundle/references/meta/documentation-inventory.md +43 -0
  26. package/bundle/references/v1-section-mapping.md +47 -0
  27. package/bundle/registry.json +39 -0
  28. package/bundle/skills/agentera/.claude-plugin/plugin.json +27 -0
  29. package/bundle/skills/agentera/SKILL.md +470 -0
  30. package/bundle/skills/agentera/agents/dokumentera.toml +6 -0
  31. package/bundle/skills/agentera/agents/hej.toml +6 -0
  32. package/bundle/skills/agentera/agents/inspektera.toml +6 -0
  33. package/bundle/skills/agentera/agents/inspirera.toml +6 -0
  34. package/bundle/skills/agentera/agents/optimera.toml +6 -0
  35. package/bundle/skills/agentera/agents/orkestrera.toml +6 -0
  36. package/bundle/skills/agentera/agents/planera.toml +6 -0
  37. package/bundle/skills/agentera/agents/profilera.toml +6 -0
  38. package/bundle/skills/agentera/agents/realisera.toml +6 -0
  39. package/bundle/skills/agentera/agents/resonera.toml +6 -0
  40. package/bundle/skills/agentera/agents/visionera.toml +6 -0
  41. package/bundle/skills/agentera/agents/visualisera.toml +6 -0
  42. package/bundle/skills/agentera/capabilities/dokumentera/instructions.md +428 -0
  43. package/bundle/skills/agentera/capabilities/dokumentera/schemas/artifacts.yaml +73 -0
  44. package/bundle/skills/agentera/capabilities/dokumentera/schemas/exit.yaml +35 -0
  45. package/bundle/skills/agentera/capabilities/dokumentera/schemas/triggers.yaml +35 -0
  46. package/bundle/skills/agentera/capabilities/dokumentera/schemas/validation.yaml +139 -0
  47. package/bundle/skills/agentera/capabilities/hej/instructions.md +331 -0
  48. package/bundle/skills/agentera/capabilities/hej/schemas/artifacts.yaml +69 -0
  49. package/bundle/skills/agentera/capabilities/hej/schemas/exit.yaml +32 -0
  50. package/bundle/skills/agentera/capabilities/hej/schemas/triggers.yaml +58 -0
  51. package/bundle/skills/agentera/capabilities/hej/schemas/validation.yaml +55 -0
  52. package/bundle/skills/agentera/capabilities/inspektera/instructions.md +514 -0
  53. package/bundle/skills/agentera/capabilities/inspektera/schemas/artifacts.yaml +76 -0
  54. package/bundle/skills/agentera/capabilities/inspektera/schemas/exit.yaml +36 -0
  55. package/bundle/skills/agentera/capabilities/inspektera/schemas/triggers.yaml +38 -0
  56. package/bundle/skills/agentera/capabilities/inspektera/schemas/validation.yaml +113 -0
  57. package/bundle/skills/agentera/capabilities/inspirera/instructions.md +280 -0
  58. package/bundle/skills/agentera/capabilities/inspirera/schemas/artifacts.yaml +24 -0
  59. package/bundle/skills/agentera/capabilities/inspirera/schemas/exit.yaml +33 -0
  60. package/bundle/skills/agentera/capabilities/inspirera/schemas/triggers.yaml +34 -0
  61. package/bundle/skills/agentera/capabilities/inspirera/schemas/validation.yaml +58 -0
  62. package/bundle/skills/agentera/capabilities/optimera/instructions.md +437 -0
  63. package/bundle/skills/agentera/capabilities/optimera/schemas/artifacts.yaml +69 -0
  64. package/bundle/skills/agentera/capabilities/optimera/schemas/exit.yaml +35 -0
  65. package/bundle/skills/agentera/capabilities/optimera/schemas/triggers.yaml +39 -0
  66. package/bundle/skills/agentera/capabilities/optimera/schemas/validation.yaml +91 -0
  67. package/bundle/skills/agentera/capabilities/orkestrera/instructions.md +433 -0
  68. package/bundle/skills/agentera/capabilities/orkestrera/schemas/artifacts.yaml +64 -0
  69. package/bundle/skills/agentera/capabilities/orkestrera/schemas/exit.yaml +34 -0
  70. package/bundle/skills/agentera/capabilities/orkestrera/schemas/triggers.yaml +42 -0
  71. package/bundle/skills/agentera/capabilities/orkestrera/schemas/validation.yaml +107 -0
  72. package/bundle/skills/agentera/capabilities/planera/instructions.md +368 -0
  73. package/bundle/skills/agentera/capabilities/planera/schemas/artifacts.yaml +62 -0
  74. package/bundle/skills/agentera/capabilities/planera/schemas/exit.yaml +33 -0
  75. package/bundle/skills/agentera/capabilities/planera/schemas/triggers.yaml +34 -0
  76. package/bundle/skills/agentera/capabilities/planera/schemas/validation.yaml +61 -0
  77. package/bundle/skills/agentera/capabilities/profilera/instructions.md +419 -0
  78. package/bundle/skills/agentera/capabilities/profilera/schemas/artifacts.yaml +18 -0
  79. package/bundle/skills/agentera/capabilities/profilera/schemas/exit.yaml +34 -0
  80. package/bundle/skills/agentera/capabilities/profilera/schemas/triggers.yaml +45 -0
  81. package/bundle/skills/agentera/capabilities/profilera/schemas/validation.yaml +57 -0
  82. package/bundle/skills/agentera/capabilities/realisera/instructions.md +403 -0
  83. package/bundle/skills/agentera/capabilities/realisera/schemas/artifacts.yaml +80 -0
  84. package/bundle/skills/agentera/capabilities/realisera/schemas/exit.yaml +35 -0
  85. package/bundle/skills/agentera/capabilities/realisera/schemas/triggers.yaml +39 -0
  86. package/bundle/skills/agentera/capabilities/realisera/schemas/validation.yaml +110 -0
  87. package/bundle/skills/agentera/capabilities/resonera/instructions.md +329 -0
  88. package/bundle/skills/agentera/capabilities/resonera/schemas/artifacts.yaml +47 -0
  89. package/bundle/skills/agentera/capabilities/resonera/schemas/exit.yaml +35 -0
  90. package/bundle/skills/agentera/capabilities/resonera/schemas/triggers.yaml +46 -0
  91. package/bundle/skills/agentera/capabilities/resonera/schemas/validation.yaml +77 -0
  92. package/bundle/skills/agentera/capabilities/visionera/instructions.md +309 -0
  93. package/bundle/skills/agentera/capabilities/visionera/schemas/artifacts.yaml +57 -0
  94. package/bundle/skills/agentera/capabilities/visionera/schemas/exit.yaml +35 -0
  95. package/bundle/skills/agentera/capabilities/visionera/schemas/triggers.yaml +41 -0
  96. package/bundle/skills/agentera/capabilities/visionera/schemas/validation.yaml +74 -0
  97. package/bundle/skills/agentera/capabilities/visualisera/instructions.md +400 -0
  98. package/bundle/skills/agentera/capabilities/visualisera/schemas/artifacts.yaml +44 -0
  99. package/bundle/skills/agentera/capabilities/visualisera/schemas/exit.yaml +34 -0
  100. package/bundle/skills/agentera/capabilities/visualisera/schemas/triggers.yaml +33 -0
  101. package/bundle/skills/agentera/capabilities/visualisera/schemas/validation.yaml +80 -0
  102. package/bundle/skills/agentera/capability_schema_contract.yaml +385 -0
  103. package/bundle/skills/agentera/protocol.yaml +463 -0
  104. package/bundle/skills/agentera/references/contract.md +1039 -0
  105. package/bundle/skills/agentera/schemas/artifacts/changelog.yaml +60 -0
  106. package/bundle/skills/agentera/schemas/artifacts/decisions.yaml +461 -0
  107. package/bundle/skills/agentera/schemas/artifacts/design.yaml +55 -0
  108. package/bundle/skills/agentera/schemas/artifacts/docs.yaml +402 -0
  109. package/bundle/skills/agentera/schemas/artifacts/experiments.yaml +373 -0
  110. package/bundle/skills/agentera/schemas/artifacts/health.yaml +484 -0
  111. package/bundle/skills/agentera/schemas/artifacts/objective.yaml +399 -0
  112. package/bundle/skills/agentera/schemas/artifacts/plan.yaml +342 -0
  113. package/bundle/skills/agentera/schemas/artifacts/progress.yaml +325 -0
  114. package/bundle/skills/agentera/schemas/artifacts/todo.yaml +110 -0
  115. package/bundle/skills/agentera/schemas/artifacts/vision.yaml +262 -0
  116. package/bundle/skills/hej/.claude-plugin/plugin.json +6 -0
  117. package/bundle/skills/hej/SKILL.md +69 -0
  118. package/bundle/skills/hej/agents/hej.toml +11 -0
  119. package/bundle/skills/hej/agents/openai.yaml +8 -0
  120. package/dist/analytics/extractCorpus.js +1791 -0
  121. package/dist/analytics/extractCorpus.js.map +1 -0
  122. package/dist/analytics/usageStats.js +487 -0
  123. package/dist/analytics/usageStats.js.map +1 -0
  124. package/dist/bin/agentera.js +4 -0
  125. package/dist/bin/agentera.js.map +1 -0
  126. package/dist/cli/appContext.js +226 -0
  127. package/dist/cli/appContext.js.map +1 -0
  128. package/dist/cli/argvalidate.js +41 -0
  129. package/dist/cli/argvalidate.js.map +1 -0
  130. package/dist/cli/capabilityContext.js +2421 -0
  131. package/dist/cli/capabilityContext.js.map +1 -0
  132. package/dist/cli/commands/backfill.js +84 -0
  133. package/dist/cli/commands/backfill.js.map +1 -0
  134. package/dist/cli/commands/capability.js +44 -0
  135. package/dist/cli/commands/capability.js.map +1 -0
  136. package/dist/cli/commands/compact.js +148 -0
  137. package/dist/cli/commands/compact.js.map +1 -0
  138. package/dist/cli/commands/doctor.js +180 -0
  139. package/dist/cli/commands/doctor.js.map +1 -0
  140. package/dist/cli/commands/lint.js +179 -0
  141. package/dist/cli/commands/lint.js.map +1 -0
  142. package/dist/cli/commands/prime.js +544 -0
  143. package/dist/cli/commands/prime.js.map +1 -0
  144. package/dist/cli/commands/query.js +346 -0
  145. package/dist/cli/commands/query.js.map +1 -0
  146. package/dist/cli/commands/report.js +210 -0
  147. package/dist/cli/commands/report.js.map +1 -0
  148. package/dist/cli/commands/schema.js +306 -0
  149. package/dist/cli/commands/schema.js.map +1 -0
  150. package/dist/cli/commands/state.js +1012 -0
  151. package/dist/cli/commands/state.js.map +1 -0
  152. package/dist/cli/commands/upgrade.js +48 -0
  153. package/dist/cli/commands/upgrade.js.map +1 -0
  154. package/dist/cli/commands/validate.js +519 -0
  155. package/dist/cli/commands/validate.js.map +1 -0
  156. package/dist/cli/commands/verify.js +204 -0
  157. package/dist/cli/commands/verify.js.map +1 -0
  158. package/dist/cli/dispatch.js +958 -0
  159. package/dist/cli/dispatch.js.map +1 -0
  160. package/dist/cli/orientation.js +595 -0
  161. package/dist/cli/orientation.js.map +1 -0
  162. package/dist/cli/prime-blob.js +3 -0
  163. package/dist/cli/prime-blob.js.map +1 -0
  164. package/dist/cli/stateQuery.js +292 -0
  165. package/dist/cli/stateQuery.js.map +1 -0
  166. package/dist/cli/structured.js +18 -0
  167. package/dist/cli/structured.js.map +1 -0
  168. package/dist/core/difflib.js +274 -0
  169. package/dist/core/difflib.js.map +1 -0
  170. package/dist/core/git.js +43 -0
  171. package/dist/core/git.js.map +1 -0
  172. package/dist/core/paths.js +50 -0
  173. package/dist/core/paths.js.map +1 -0
  174. package/dist/core/pyjson.js +101 -0
  175. package/dist/core/pyjson.js.map +1 -0
  176. package/dist/core/sourceRoot.js +72 -0
  177. package/dist/core/sourceRoot.js.map +1 -0
  178. package/dist/core/toml.js +11 -0
  179. package/dist/core/toml.js.map +1 -0
  180. package/dist/core/yaml.js +25 -0
  181. package/dist/core/yaml.js.map +1 -0
  182. package/dist/eval/evalSkills.js +258 -0
  183. package/dist/eval/evalSkills.js.map +1 -0
  184. package/dist/eval/semanticEval.js +148 -0
  185. package/dist/eval/semanticEval.js.map +1 -0
  186. package/dist/eval/semanticFixtures.js +227 -0
  187. package/dist/eval/semanticFixtures.js.map +1 -0
  188. package/dist/hooks/common.js +160 -0
  189. package/dist/hooks/common.js.map +1 -0
  190. package/dist/hooks/compaction.js +935 -0
  191. package/dist/hooks/compaction.js.map +1 -0
  192. package/dist/hooks/cursorPreToolUse.js +19 -0
  193. package/dist/hooks/cursorPreToolUse.js.map +1 -0
  194. package/dist/hooks/cursorSessionStart.js +71 -0
  195. package/dist/hooks/cursorSessionStart.js.map +1 -0
  196. package/dist/hooks/sessionStart.js +209 -0
  197. package/dist/hooks/sessionStart.js.map +1 -0
  198. package/dist/hooks/sessionStop.js +212 -0
  199. package/dist/hooks/sessionStop.js.map +1 -0
  200. package/dist/hooks/validateArtifact.js +933 -0
  201. package/dist/hooks/validateArtifact.js.map +1 -0
  202. package/dist/registries/artifactRegistry.js +206 -0
  203. package/dist/registries/artifactRegistry.js.map +1 -0
  204. package/dist/registries/capabilityContract.js +310 -0
  205. package/dist/registries/capabilityContract.js.map +1 -0
  206. package/dist/registries/packageRegistry.js +641 -0
  207. package/dist/registries/packageRegistry.js.map +1 -0
  208. package/dist/registries/runtimeAdapterRegistry.js +315 -0
  209. package/dist/registries/runtimeAdapterRegistry.js.map +1 -0
  210. package/dist/setup/codex.js +1056 -0
  211. package/dist/setup/codex.js.map +1 -0
  212. package/dist/setup/copilot.js +227 -0
  213. package/dist/setup/copilot.js.map +1 -0
  214. package/dist/setup/cursor.js +127 -0
  215. package/dist/setup/cursor.js.map +1 -0
  216. package/dist/setup/doctor.js +1276 -0
  217. package/dist/setup/doctor.js.map +1 -0
  218. package/dist/state/installRoot.js +279 -0
  219. package/dist/state/installRoot.js.map +1 -0
  220. package/dist/state/progressCommit.js +289 -0
  221. package/dist/state/progressCommit.js.map +1 -0
  222. package/dist/state/startupAnalysis.js +1953 -0
  223. package/dist/state/startupAnalysis.js.map +1 -0
  224. package/dist/upgrade/appModel.js +189 -0
  225. package/dist/upgrade/appModel.js.map +1 -0
  226. package/dist/upgrade/channels.js +208 -0
  227. package/dist/upgrade/channels.js.map +1 -0
  228. package/dist/upgrade/compatibility.js +201 -0
  229. package/dist/upgrade/compatibility.js.map +1 -0
  230. package/dist/upgrade/doctor.js +373 -0
  231. package/dist/upgrade/doctor.js.map +1 -0
  232. package/dist/upgrade/migrateArtifactsV2ToV3.js +332 -0
  233. package/dist/upgrade/migrateArtifactsV2ToV3.js.map +1 -0
  234. package/dist/upgrade/runtimeMigration.js +484 -0
  235. package/dist/upgrade/runtimeMigration.js.map +1 -0
  236. package/dist/upgrade/upgradeCommands.js +36 -0
  237. package/dist/upgrade/upgradeCommands.js.map +1 -0
  238. package/dist/upgrade/upgradeOrchestrator.js +299 -0
  239. package/dist/upgrade/upgradeOrchestrator.js.map +1 -0
  240. package/dist/upgrade/versionResolution.js +179 -0
  241. package/dist/upgrade/versionResolution.js.map +1 -0
  242. package/dist/validate/appHomeContract.js +150 -0
  243. package/dist/validate/appHomeContract.js.map +1 -0
  244. package/dist/validate/capability.js +412 -0
  245. package/dist/validate/capability.js.map +1 -0
  246. package/dist/validate/crossCapability.js +145 -0
  247. package/dist/validate/crossCapability.js.map +1 -0
  248. package/dist/validate/lifecycleAdapters.js +772 -0
  249. package/dist/validate/lifecycleAdapters.js.map +1 -0
  250. package/dist/validate/selfAudit.js +107 -0
  251. package/dist/validate/selfAudit.js.map +1 -0
  252. package/package.json +28 -8
  253. package/LICENSE +0 -201
  254. package/bin/agentera.mjs +0 -50
  255. package/lib/exec.mjs +0 -116
  256. package/lib/resolve.mjs +0 -129
@@ -0,0 +1,772 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+ import { resolvePath } from "../core/paths.js";
5
+ import { resolveSourceRoot } from "../core/sourceRoot.js";
6
+ import { loadRegistry as loadPackageRegistry, } from "../registries/packageRegistry.js";
7
+ import { RegistryError, loadRegistry as loadRuntimeRegistry, } from "../registries/runtimeAdapterRegistry.js";
8
+ const REGISTRY_CONTRACT_ERROR_PREFIX = "registry contract error";
9
+ const CODEX_PROFILERA_TERMS = [
10
+ "allow_implicit_invocation: false",
11
+ "codex_session_corpus",
12
+ "bounded Codex history, session, or config corpus data",
13
+ ];
14
+ const CODEX_AGENTERA_METADATA_TERMS = ["$agentera", "bounded Codex session corpus data", "AGENTERA_HOME"];
15
+ const CODEX_PROFILERA_STATUS_VALUES = ["ok", "degraded"];
16
+ const UV_SCRIPT_SHEBANG = "#!/usr/bin/env -S uv run --script";
17
+ const UV_INSTALL_GUIDANCE = "uv is required to run packaged Agentera Python scripts; install it from " +
18
+ "https://docs.astral.sh/uv/getting-started/installation/ and then rerun the check";
19
+ const HARD_GATE_DOC_REQUIREMENTS = {
20
+ "references/adapters/runtime-feature-parity.md": ["opencode", "copilot", "cursor"],
21
+ "references/adapters/opencode.md": ["opencode"],
22
+ };
23
+ function rootDefault() {
24
+ return resolveSourceRoot();
25
+ }
26
+ function packageRegistryPath(root) {
27
+ return path.join(root, "references/adapters/package-registry.yaml");
28
+ }
29
+ function isMapping(v) {
30
+ return v !== null && typeof v === "object" && !Array.isArray(v);
31
+ }
32
+ function loadJson(p) {
33
+ const data = JSON.parse(fs.readFileSync(p, "utf8"));
34
+ if (!isMapping(data)) {
35
+ throw new Error(`${p}: expected JSON object`);
36
+ }
37
+ return data;
38
+ }
39
+ function registryContractError(exc) {
40
+ return `${REGISTRY_CONTRACT_ERROR_PREFIX}: ${exc.message}`;
41
+ }
42
+ function runtimeView(registry, runtime) {
43
+ return registry.consumerView("lifecycle", runtime);
44
+ }
45
+ function packageManifest(root) {
46
+ const registryPath = packageRegistryPath(root);
47
+ if (fs.existsSync(registryPath) && fs.statSync(registryPath).isFile()) {
48
+ return loadPackageRegistry(registryPath, root);
49
+ }
50
+ return loadPackageRegistry(packageRegistryPath(rootDefault()), rootDefault());
51
+ }
52
+ function supportedEvents(registry, runtime) {
53
+ return new Set(runtimeView(registry, runtime).lifecycle_events.supported_events);
54
+ }
55
+ function unsupportedEvents(registry, runtime) {
56
+ return new Set(runtimeView(registry, runtime).lifecycle_events.unsupported_events);
57
+ }
58
+ function opencodeEventPayloadTypes(registry) {
59
+ const eventStatus = runtimeView(registry, "opencode").lifecycle_events.event_status;
60
+ const out = new Set();
61
+ for (const [event, status] of Object.entries(eventStatus)) {
62
+ if (status === "supported_via_event")
63
+ out.add(event);
64
+ }
65
+ return out;
66
+ }
67
+ function validationEvents(registry, runtime) {
68
+ return runtimeView(registry, runtime).artifact_validation.validation_events;
69
+ }
70
+ function claimTerms(text, candidates) {
71
+ const normalized = text.toLowerCase();
72
+ return candidates.filter((term) => normalized.includes(term.toLowerCase()));
73
+ }
74
+ function copilotProfileraTerms(registry) {
75
+ const view = runtimeView(registry, "copilot");
76
+ const text = [...view.lifecycle_events.limitations, ...view.documentation_claims.parity_claims].join(" ");
77
+ return claimTerms(text, ["profilera", "bounded", "corpus", "metadata", "missing source families"]);
78
+ }
79
+ function codexLifecycleStatusValues(registry) {
80
+ const eventStatus = runtimeView(registry, "codex").lifecycle_events.event_status;
81
+ const statuses = ["stable"];
82
+ for (const event of runtimeView(registry, "codex").lifecycle_events.supported_events) {
83
+ const status = eventStatus[event];
84
+ if (typeof status === "string" && status !== "unsupported" && !statuses.includes(status)) {
85
+ statuses.push(status);
86
+ }
87
+ }
88
+ return statuses;
89
+ }
90
+ function codexLimitationTerms(registry) {
91
+ const view = runtimeView(registry, "codex");
92
+ const text = [...view.lifecycle_events.limitations, ...view.artifact_validation.hard_gate_claims].join(" ");
93
+ return claimTerms(text, ["codex_hooks", "apply_patch", "openai/codex#18391"]);
94
+ }
95
+ function hardGateDocTerms(registry, runtime, relativePath) {
96
+ const view = runtimeView(registry, runtime);
97
+ const artifact = view.artifact_validation;
98
+ const primary = relativePath === "references/adapters/opencode.md" ? view.documentation_claims.parity_claims : artifact.hard_gate_claims;
99
+ return [...primary, ...artifact.payload_reconstruction_limitations];
100
+ }
101
+ function validateCommandHandler(errors, runtime, event, index, handler) {
102
+ const prefix = `${runtime}.${event}[${index}]`;
103
+ if (!isMapping(handler)) {
104
+ errors.push(`${prefix}: handler must be an object`);
105
+ return;
106
+ }
107
+ if (handler.type !== "command") {
108
+ errors.push(`${prefix}: handler type must be 'command'`);
109
+ }
110
+ if ("command" in handler) {
111
+ errors.push(`${prefix}: use bash/powershell, not Claude-style command`);
112
+ }
113
+ if (!handler.bash && !handler.powershell) {
114
+ errors.push(`${prefix}: handler must define bash or powershell`);
115
+ }
116
+ const timeout = handler.timeoutSec;
117
+ if (timeout !== null && timeout !== undefined && !(typeof timeout === "number" && Number.isInteger(timeout))) {
118
+ errors.push(`${prefix}: timeoutSec must be an integer`);
119
+ }
120
+ }
121
+ function handlerCommandText(handler) {
122
+ if (!isMapping(handler))
123
+ return "";
124
+ return [handler.bash, handler.powershell].filter((p) => typeof p === "string").join(" ");
125
+ }
126
+ function stringPaths(value) {
127
+ if (typeof value === "string")
128
+ return [value];
129
+ if (Array.isArray(value) && value.every((p) => typeof p === "string"))
130
+ return value;
131
+ return [];
132
+ }
133
+ function resolveInside(root, p) {
134
+ const resolved = resolvePath(path.join(root, p));
135
+ const rel = path.relative(resolvePath(root), resolved);
136
+ if (rel !== "" && (rel.startsWith("..") || path.isAbsolute(rel)))
137
+ return null;
138
+ return resolved;
139
+ }
140
+ function resolveFromManifest(root, manifest, p) {
141
+ const resolved = resolvePath(path.join(path.dirname(manifest), p));
142
+ const rel = path.relative(resolvePath(root), resolved);
143
+ if (rel !== "" && (rel.startsWith("..") || path.isAbsolute(rel)))
144
+ return null;
145
+ return resolved;
146
+ }
147
+ function isFile(p) {
148
+ try {
149
+ return fs.statSync(p).isFile();
150
+ }
151
+ catch {
152
+ return false;
153
+ }
154
+ }
155
+ function isDir(p) {
156
+ try {
157
+ return fs.statSync(p).isDirectory();
158
+ }
159
+ catch {
160
+ return false;
161
+ }
162
+ }
163
+ function isPackagedPythonScript(p) {
164
+ if (!isFile(p))
165
+ return false;
166
+ const suffix = path.extname(p);
167
+ if (suffix === ".py")
168
+ return true;
169
+ if (suffix)
170
+ return false;
171
+ const firstLine = fs.readFileSync(p, "utf8").split(/\r\n|\r|\n/)[0] ?? "";
172
+ return Boolean(firstLine) && (firstLine.includes("python") || firstLine.includes("uv run --script"));
173
+ }
174
+ function packagedPythonScripts(root) {
175
+ const paths = [];
176
+ for (const directory of ["scripts", "hooks"]) {
177
+ const scriptRoot = path.join(root, directory);
178
+ if (!isDir(scriptRoot))
179
+ continue;
180
+ for (const name of fs.readdirSync(scriptRoot)) {
181
+ const p = path.join(scriptRoot, name);
182
+ if (isPackagedPythonScript(p))
183
+ paths.push(p);
184
+ }
185
+ }
186
+ return paths.sort();
187
+ }
188
+ function extractInlineScriptMetadata(text) {
189
+ const lines = text.split(/\r\n|\r|\n/);
190
+ const start = lines.indexOf("# /// script");
191
+ if (start === -1)
192
+ return null;
193
+ for (let index = start + 1; index < lines.length; index++) {
194
+ if (lines[index] === "# ///") {
195
+ return lines.slice(start + 1, index);
196
+ }
197
+ }
198
+ return null;
199
+ }
200
+ function metadataDeclaresRequiresPython(metadata) {
201
+ return metadata.some((line) => line.trim().startsWith("# requires-python = "));
202
+ }
203
+ function metadataDeclaresDependencies(metadata) {
204
+ return metadata.some((line) => line.trim().startsWith("# dependencies = ["));
205
+ }
206
+ export function validatePackagedPythonScripts(root) {
207
+ const errors = [];
208
+ for (const p of packagedPythonScripts(root)) {
209
+ const relative = path.relative(root, p);
210
+ const text = fs.readFileSync(p, "utf8");
211
+ const lines = text.split(/\r\n|\r|\n/);
212
+ const firstLine = lines.length > 0 ? lines[0] : "";
213
+ if (firstLine !== UV_SCRIPT_SHEBANG) {
214
+ errors.push(`${relative}: packaged Python script must use uv script shebang`);
215
+ }
216
+ const metadata = extractInlineScriptMetadata(text);
217
+ if (metadata === null) {
218
+ errors.push(`${relative}: packaged Python script must declare inline script metadata`);
219
+ continue;
220
+ }
221
+ if (!metadataDeclaresRequiresPython(metadata)) {
222
+ errors.push(`${relative}: packaged Python script must declare requires-python`);
223
+ }
224
+ if (!metadataDeclaresDependencies(metadata)) {
225
+ errors.push(`${relative}: packaged Python script must declare dependencies`);
226
+ }
227
+ }
228
+ return errors;
229
+ }
230
+ export function validateUvRuntime() {
231
+ const result = spawnSync(process.platform === "win32" ? "where" : "which", ["uv"], { encoding: "utf8" });
232
+ if (result.status !== 0)
233
+ return [UV_INSTALL_GUIDANCE];
234
+ return [];
235
+ }
236
+ export function validateSuiteBundleSurface(root, runtimeNames = null, packageRegistry = null) {
237
+ const errors = [];
238
+ const pkg = packageRegistry ?? packageManifest(root);
239
+ const runtimePackageSurfaces = pkg.runtimeManifestPaths();
240
+ const requiredPaths = pkg.sharedPathRequirements();
241
+ const packageShapes = pkg.runtimePackageShapes();
242
+ const active = runtimeNames ?? new Set(Object.keys(runtimePackageSurfaces));
243
+ for (const runtime of [...active].sort()) {
244
+ const relativeManifest = runtimePackageSurfaces[runtime];
245
+ if (relativeManifest === undefined) {
246
+ errors.push(`${runtime}: unknown runtime package surface`);
247
+ continue;
248
+ }
249
+ const manifest = path.join(root, relativeManifest);
250
+ if (!isFile(manifest)) {
251
+ errors.push(`${runtime}: missing package metadata ${relativeManifest}`);
252
+ continue;
253
+ }
254
+ let pkgJson;
255
+ try {
256
+ pkgJson = loadJson(manifest);
257
+ }
258
+ catch (exc) {
259
+ errors.push(`${runtime}: could not read package metadata ${relativeManifest}: ${exc.message}`);
260
+ continue;
261
+ }
262
+ let metadata = pkgJson.agentera;
263
+ if (!isMapping(metadata) && runtime === "claude") {
264
+ const plugins = pkgJson.plugins;
265
+ if (Array.isArray(plugins)) {
266
+ metadata =
267
+ plugins.find((plugin) => isMapping(plugin) && plugin.name === "agentera" && isMapping(plugin.agentera))?.agentera ?? null;
268
+ }
269
+ }
270
+ if (!isMapping(metadata)) {
271
+ errors.push(`${runtime}: missing agentera app metadata`);
272
+ continue;
273
+ }
274
+ const expectedShape = packageShapes[runtime];
275
+ if (metadata.packageShape !== expectedShape) {
276
+ errors.push(`${runtime}: agentera.packageShape must be ${expectedShape}`);
277
+ }
278
+ const installRootValue = metadata.installRoot;
279
+ if (typeof installRootValue !== "string" || !installRootValue) {
280
+ errors.push(`${runtime}: agentera.installRoot must point at the Agentera app root`);
281
+ continue;
282
+ }
283
+ const installRoot = resolveFromManifest(root, manifest, installRootValue);
284
+ if (installRoot === null) {
285
+ errors.push(`${runtime}: agentera.installRoot must stay inside package root`);
286
+ continue;
287
+ }
288
+ if (installRoot !== resolvePath(root)) {
289
+ errors.push(`${runtime}: AGENTERA_HOME must resolve to the package root`);
290
+ }
291
+ const sharedPaths = metadata.sharedPaths;
292
+ if (!Array.isArray(sharedPaths) || !sharedPaths.every((p) => typeof p === "string")) {
293
+ errors.push(`${runtime}: agentera.sharedPaths must list app paths`);
294
+ continue;
295
+ }
296
+ const listed = new Set(sharedPaths);
297
+ for (const [p, expectedKind] of Object.entries(requiredPaths)) {
298
+ if (!listed.has(p)) {
299
+ errors.push(`${runtime}: shared tool path ${p} missing from package metadata`);
300
+ continue;
301
+ }
302
+ const resolved = resolveInside(installRoot, p);
303
+ if (resolved === null) {
304
+ errors.push(`${runtime}: shared tool path ${p} must stay inside the Agentera app root`);
305
+ }
306
+ else if (expectedKind === "dir" && !isDir(resolved)) {
307
+ errors.push(`${runtime}: shared tool path ${p} must resolve to a directory`);
308
+ }
309
+ else if (expectedKind === "file" && !isFile(resolved)) {
310
+ errors.push(`${runtime}: shared tool path ${p} must resolve to a file`);
311
+ }
312
+ }
313
+ const singleSkill = metadata.singleSkillInstall;
314
+ if (typeof singleSkill !== "string" || !singleSkill.includes("core") || !singleSkill.includes("suite")) {
315
+ errors.push(`${runtime}: agentera.singleSkillInstall must state core skill behavior does not require suite tools`);
316
+ }
317
+ }
318
+ return errors;
319
+ }
320
+ export function validateCopilot(plugin, pluginRoot, registry = loadRuntimeRegistry()) {
321
+ const errors = [];
322
+ if ("lifecycleHooks" in plugin) {
323
+ errors.push("copilot: use supported hooks component field, not lifecycleHooks");
324
+ }
325
+ const skills = plugin.skills;
326
+ if (!(typeof skills === "string" || Array.isArray(skills))) {
327
+ errors.push("copilot.skills must be a string or string array path");
328
+ }
329
+ else if (Array.isArray(skills) && !skills.every((p) => typeof p === "string")) {
330
+ errors.push("copilot.skills entries must be path strings");
331
+ }
332
+ else {
333
+ for (const p of stringPaths(skills)) {
334
+ const resolved = resolveInside(pluginRoot, p);
335
+ if (resolved === null)
336
+ errors.push("copilot.skills paths must stay inside plugin root");
337
+ else if (!isDir(resolved))
338
+ errors.push("copilot.skills paths must resolve to skill directories");
339
+ }
340
+ }
341
+ const hooks = plugin.hooks;
342
+ if (!(typeof hooks === "string" || Array.isArray(hooks))) {
343
+ errors.push("copilot.hooks must be a string or string array path");
344
+ }
345
+ else if (Array.isArray(hooks) && !hooks.every((p) => typeof p === "string")) {
346
+ errors.push("copilot.hooks entries must be path strings");
347
+ }
348
+ else {
349
+ for (const p of stringPaths(hooks)) {
350
+ const resolved = resolveInside(pluginRoot, p);
351
+ if (resolved === null)
352
+ errors.push("copilot.hooks paths must stay inside plugin root");
353
+ else if (!isDir(resolved))
354
+ errors.push("copilot.hooks paths must resolve to a hook directory");
355
+ }
356
+ }
357
+ const description = plugin.description;
358
+ const profileraTerms = copilotProfileraTerms(registry);
359
+ if (typeof description !== "string" || profileraTerms.some((term) => !description.includes(term))) {
360
+ errors.push("copilot.profilera: description must expose bounded corpus metadata limits");
361
+ }
362
+ return errors;
363
+ }
364
+ export function validateCopilotHooks(pluginRoot, plugin, registry = loadRuntimeRegistry()) {
365
+ const errors = [];
366
+ const copilotEvents = supportedEvents(registry, "copilot");
367
+ const requiredPrewriteHook = validationEvents(registry, "copilot")[0] ?? "";
368
+ const hookPaths = stringPaths(plugin.hooks);
369
+ if (hookPaths.length === 0)
370
+ return errors;
371
+ for (const hooks of hookPaths) {
372
+ const hookDir = resolveInside(pluginRoot, hooks);
373
+ if (hookDir === null) {
374
+ errors.push("copilot.hooks paths must stay inside plugin root");
375
+ continue;
376
+ }
377
+ if (!isDir(hookDir)) {
378
+ errors.push("copilot.hooks must resolve to a hook directory");
379
+ continue;
380
+ }
381
+ const seenEvents = new Set();
382
+ const jsonFiles = fs
383
+ .readdirSync(hookDir)
384
+ .filter((n) => n.endsWith(".json"))
385
+ .sort();
386
+ for (const name of jsonFiles) {
387
+ const hook = loadJson(path.join(hookDir, name));
388
+ const stem = name.replace(/\.json$/, "");
389
+ const event = hook.name;
390
+ if (!copilotEvents.has(stem)) {
391
+ errors.push(`copilot: unsupported lifecycle hook file configured: ${name}`);
392
+ }
393
+ if (typeof event !== "string") {
394
+ errors.push(`copilot.${name}: hook name must be a string`);
395
+ continue;
396
+ }
397
+ if (!copilotEvents.has(event)) {
398
+ errors.push(`copilot: unsupported lifecycle event configured: ${event}`);
399
+ continue;
400
+ }
401
+ if (stem !== event) {
402
+ errors.push(`copilot.${name}: hook filename must match event name ${event}`);
403
+ }
404
+ if (event !== event.slice(0, 1).toLowerCase() + event.slice(1)) {
405
+ errors.push(`copilot: event must be lower-camel: ${event}`);
406
+ }
407
+ validateCommandHandler(errors, "copilot", event, 0, hook);
408
+ seenEvents.add(event);
409
+ if (event === requiredPrewriteHook) {
410
+ const commandText = handlerCommandText(hook);
411
+ if (!commandText.includes("hook validate-artifact")) {
412
+ errors.push("copilot.preToolUse: artifact hard gate must run agentera hook validate-artifact");
413
+ }
414
+ }
415
+ }
416
+ if (!seenEvents.has(requiredPrewriteHook)) {
417
+ errors.push("copilot: missing required preToolUse artifact validation hook");
418
+ }
419
+ }
420
+ return errors;
421
+ }
422
+ export function validateCursor(root, plugin, registry = loadRuntimeRegistry()) {
423
+ void registry;
424
+ const errors = [];
425
+ const cursorMeta = plugin.cursor;
426
+ if (!isMapping(cursorMeta)) {
427
+ errors.push("cursor: missing cursor metadata object");
428
+ }
429
+ else {
430
+ const limitations = cursorMeta.limitations;
431
+ if (!Array.isArray(limitations) || limitations.length === 0) {
432
+ errors.push("cursor: limitations must document cloud agents, bare hej, and hard-gate gating");
433
+ }
434
+ else {
435
+ const joined = limitations.map((item) => String(item)).join(" ");
436
+ for (const term of ["Cloud agents", "bare hej", "hard-gate", "smoke"]) {
437
+ if (!joined.toLowerCase().includes(term.toLowerCase())) {
438
+ errors.push(`cursor: limitations must mention '${term}'`);
439
+ }
440
+ }
441
+ }
442
+ }
443
+ const skillPaths = stringPaths(plugin.skills);
444
+ if (skillPaths.length === 0) {
445
+ errors.push("cursor.skills must be a string or string array path");
446
+ }
447
+ for (const skillPath of skillPaths) {
448
+ const resolved = resolveInside(root, skillPath.replace(/^\.\//, ""));
449
+ if (resolved === null) {
450
+ errors.push("cursor.skills paths must stay inside repository root");
451
+ }
452
+ }
453
+ const hookPaths = stringPaths(plugin.hooks);
454
+ if (hookPaths.length === 0) {
455
+ errors.push("cursor.hooks must reference bundled hooks.json");
456
+ }
457
+ for (const hookPath of hookPaths) {
458
+ const resolved = resolveInside(root, hookPath.replace(/^\.\//, ""));
459
+ if (resolved === null || !isFile(resolved)) {
460
+ errors.push("cursor.hooks must resolve to .cursor/hooks.json");
461
+ }
462
+ }
463
+ const agentPaths = stringPaths(plugin.agents);
464
+ if (agentPaths.length === 0) {
465
+ errors.push("cursor.agents must reference managed capability agents");
466
+ }
467
+ else {
468
+ for (const agentPath of agentPaths) {
469
+ const resolved = resolveInside(root, agentPath.replace(/^\.\//, ""));
470
+ if (resolved === null || !isDir(resolved)) {
471
+ errors.push("cursor.agents must resolve to .cursor/agents");
472
+ }
473
+ else if (fs.readdirSync(resolved).filter((n) => n.endsWith(".md")).length < 12) {
474
+ errors.push("cursor.agents must expose twelve managed capability descriptors");
475
+ }
476
+ }
477
+ }
478
+ const reference = path.join(root, "references/adapters/cursor.md");
479
+ if (!isFile(reference)) {
480
+ errors.push("cursor: missing references/adapters/cursor.md");
481
+ }
482
+ else {
483
+ const text = fs.readFileSync(reference, "utf8").toLowerCase();
484
+ for (const term of ["cloud agents", "cursor-agent", "metadata-only", "live pretooluse write smoke"]) {
485
+ if (!text.includes(term)) {
486
+ errors.push(`cursor.md must document '${term}'`);
487
+ }
488
+ }
489
+ }
490
+ return errors;
491
+ }
492
+ export function validateCursorHooks(root, registry = loadRuntimeRegistry()) {
493
+ const errors = [];
494
+ const cursorEvents = supportedEvents(registry, "cursor");
495
+ const requiredPrewriteHook = validationEvents(registry, "cursor")[0] ?? "";
496
+ const hooksPath = path.join(root, ".cursor", "hooks.json");
497
+ if (!isFile(hooksPath))
498
+ return ["cursor: missing .cursor/hooks.json"];
499
+ const payload = loadJson(hooksPath);
500
+ const hooks = payload.hooks;
501
+ if (!isMapping(hooks))
502
+ return [...errors, "cursor: hooks.json must contain a hooks object"];
503
+ const configured = new Set();
504
+ for (const [event, entries] of Object.entries(hooks)) {
505
+ if (!cursorEvents.has(event)) {
506
+ errors.push(`cursor: unsupported lifecycle event configured: ${event}`);
507
+ continue;
508
+ }
509
+ if (!Array.isArray(entries)) {
510
+ errors.push(`cursor.${event}: hook entries must be a list`);
511
+ continue;
512
+ }
513
+ configured.add(event);
514
+ entries.forEach((entry, index) => {
515
+ if (!isMapping(entry)) {
516
+ errors.push(`cursor.${event}[${index}]: hook entry must be an object`);
517
+ return;
518
+ }
519
+ const command = entry.command;
520
+ if (typeof command !== "string" || !command.trim()) {
521
+ errors.push(`cursor.${event}[${index}]: command must be a string`);
522
+ return;
523
+ }
524
+ if (event === "sessionStart" && !command.includes("hook cursor-session-start")) {
525
+ errors.push("cursor.sessionStart: must run agentera hook cursor-session-start");
526
+ }
527
+ if (event === requiredPrewriteHook && !command.includes("hook cursor-pre-tool-use")) {
528
+ errors.push("cursor.preToolUse: artifact hard gate must run agentera hook cursor-pre-tool-use");
529
+ }
530
+ });
531
+ }
532
+ for (const required of ["sessionStart", "sessionEnd", requiredPrewriteHook, "postToolUse"]) {
533
+ if (required && !configured.has(required)) {
534
+ errors.push(`cursor: missing required lifecycle hook ${required}`);
535
+ }
536
+ }
537
+ return errors;
538
+ }
539
+ export function validateCodex(plugin, registry = loadRuntimeRegistry()) {
540
+ const errors = [];
541
+ const codexEvents = supportedEvents(registry, "codex");
542
+ const unsupportedCodexEvents = unsupportedEvents(registry, "codex");
543
+ const lifecycleStatusValues = codexLifecycleStatusValues(registry);
544
+ const limitationTerms = codexLimitationTerms(registry);
545
+ const lifecycle = plugin.lifecycleHooks;
546
+ if (!isMapping(lifecycle))
547
+ return ["codex: missing lifecycleHooks limitation metadata"];
548
+ if (lifecycle.configured !== false)
549
+ errors.push("codex: lifecycleHooks.configured must be false");
550
+ if (!lifecycleStatusValues.includes(lifecycle.status)) {
551
+ errors.push("codex: lifecycleHooks.status must be one of " + lifecycleStatusValues.join(", "));
552
+ }
553
+ const events = lifecycle.events ?? {};
554
+ if (!isMapping(events)) {
555
+ errors.push("codex: lifecycleHooks.events must be an object when present");
556
+ }
557
+ else {
558
+ for (const event of Object.keys(events)) {
559
+ if (!codexEvents.has(event))
560
+ errors.push(`codex: unsupported lifecycle event configured: ${event}`);
561
+ }
562
+ }
563
+ const supported = lifecycle.supportedEvents;
564
+ const codexSupportedList = runtimeView(registry, "codex").lifecycle_events.supported_events;
565
+ if (!Array.isArray(supported) || !setsEqual(new Set(supported), codexEvents)) {
566
+ errors.push("codex: supportedEvents must list every Codex codex_hooks event (" + codexSupportedList.join(", ") + ")");
567
+ }
568
+ const unsupported = lifecycle.unsupportedEvents;
569
+ if (!Array.isArray(unsupported) || unsupported.length === 0) {
570
+ errors.push("codex: unsupportedEvents must list Claude-Code-specific events with no Codex equivalent");
571
+ }
572
+ else {
573
+ for (const entry of unsupported) {
574
+ if (!isMapping(entry)) {
575
+ errors.push("codex: unsupportedEvents entries must be objects with event and reason fields");
576
+ continue;
577
+ }
578
+ const event = entry.event;
579
+ if (codexEvents.has(event)) {
580
+ errors.push(`codex: unsupportedEvents must not list event '${event}' that codex_hooks now supports`);
581
+ }
582
+ else if (!unsupportedCodexEvents.has(event)) {
583
+ errors.push(`codex: unsupportedEvents entry '${event}' is not claimed by the registry`);
584
+ }
585
+ }
586
+ }
587
+ const limitations = lifecycle.limitations;
588
+ if (!Array.isArray(limitations) || limitations.length === 0) {
589
+ errors.push("codex: limitations must document codex_hooks status and apply_patch interception");
590
+ }
591
+ else {
592
+ const joined = limitations.filter((item) => typeof item === "string").join(" ");
593
+ for (const term of limitationTerms) {
594
+ if (!joined.includes(term)) {
595
+ errors.push(`codex: limitations must cite '${term}' so apply_patch interception ground truth stays surfaced`);
596
+ }
597
+ }
598
+ for (const marker of ["experimental, require host config opt-in", "experimental-disabled", "no real-time"]) {
599
+ if (joined.includes(marker)) {
600
+ errors.push(`codex: limitations carry stale wording '${marker}'; remove it`);
601
+ }
602
+ }
603
+ }
604
+ return errors;
605
+ }
606
+ export function validateCodexProfileraMetadata(root, plugin) {
607
+ const errors = [];
608
+ const agentera = (plugin.skillMetadata ?? []).find((skill) => isMapping(skill) && skill.name === "agentera");
609
+ if (!isMapping(agentera))
610
+ return ["codex.agentera: missing aggregate Agentera app metadata"];
611
+ if (agentera.runtimeSupport !== "portable")
612
+ errors.push("codex.agentera: runtimeSupport must stay portable");
613
+ if ((agentera.policy ?? {}).allow_implicit_invocation !== true) {
614
+ errors.push("codex.agentera: Agentera app entry must allow implicit invocation");
615
+ }
616
+ const invocationHint = agentera.invocationHint;
617
+ if (typeof invocationHint !== "string" || !invocationHint.includes("$agentera")) {
618
+ errors.push("codex.agentera: invocation hint must name $agentera");
619
+ }
620
+ const codex = plugin.codex;
621
+ const codexText = isMapping(codex) ? (codex.limitations ?? []).join(" ") : "";
622
+ for (const term of CODEX_AGENTERA_METADATA_TERMS) {
623
+ if (!`${invocationHint || ""} ${codexText}`.includes(term)) {
624
+ errors.push(`codex.agentera: metadata must surface '${term}'`);
625
+ }
626
+ }
627
+ for (const rel of ["agents/openai.yaml"]) {
628
+ const p = path.join(root, rel);
629
+ if (!isFile(p)) {
630
+ errors.push(`codex.agentera: missing metadata surface ${rel}`);
631
+ continue;
632
+ }
633
+ const text = fs.readFileSync(p, "utf8");
634
+ if (!text.includes("path: ./skills/agentera")) {
635
+ errors.push(`codex.agentera: ${rel} must point at installed skills/agentera`);
636
+ }
637
+ for (const stalePath of ["path: ./skills/hej", "metadata: ./skills/hej", "skills/<name>/agents"]) {
638
+ if (text.includes(stalePath)) {
639
+ errors.push(`codex.agentera: ${rel} carries stale v1 skill path '${stalePath}'`);
640
+ }
641
+ }
642
+ for (const term of CODEX_PROFILERA_TERMS) {
643
+ if (!text.includes(term))
644
+ errors.push(`codex.agentera: ${rel} missing '${term}'`);
645
+ }
646
+ if (!CODEX_PROFILERA_STATUS_VALUES.some((value) => text.includes(`status: ${value}`))) {
647
+ errors.push(`codex.agentera: ${rel} missing status declaration (expected one of ` +
648
+ CODEX_PROFILERA_STATUS_VALUES.join(", ") +
649
+ ")");
650
+ }
651
+ if (text.includes("collector exists") || text.includes("not implemented")) {
652
+ errors.push(`codex.agentera: ${rel} contains stale missing-collector wording`);
653
+ }
654
+ }
655
+ return errors;
656
+ }
657
+ export function validateOpencode(root, registry = loadRuntimeRegistry()) {
658
+ const errors = [];
659
+ const pluginPath = path.join(root, ".opencode/plugins/agentera.js");
660
+ if (!isFile(pluginPath))
661
+ return ["opencode: missing .opencode/plugins/agentera.js"];
662
+ const text = fs.readFileSync(pluginPath, "utf8");
663
+ if (!text.includes("event: async")) {
664
+ errors.push("opencode: session lifecycle must use the generic event hook");
665
+ }
666
+ for (const eventType of opencodeEventPayloadTypes(registry)) {
667
+ if (!text.includes(`event.type !== "${eventType}"`) && !text.includes(`event.type === "${eventType}"`)) {
668
+ errors.push(`opencode: event hook must handle or explicitly skip ${eventType}`);
669
+ }
670
+ if (text.includes(`"${eventType}":`)) {
671
+ errors.push(`opencode: must not register direct hook ${eventType}`);
672
+ }
673
+ }
674
+ for (const eventType of unsupportedEvents(registry, "opencode")) {
675
+ if (text.includes(`"${eventType}":`)) {
676
+ errors.push(`opencode: must not register unsupported direct hook ${eventType}`);
677
+ }
678
+ }
679
+ const payloadTypes = opencodeEventPayloadTypes(registry);
680
+ const directHooks = [...supportedEvents(registry, "opencode")].filter((e) => !payloadTypes.has(e));
681
+ for (const event of directHooks) {
682
+ if (!text.includes(`"${event}"`)) {
683
+ errors.push(`opencode: missing "${event}" hook`);
684
+ }
685
+ }
686
+ if (!text.includes("validateArtifactCandidate")) {
687
+ errors.push("opencode: tool.execute.before must validate artifact candidates");
688
+ }
689
+ return errors;
690
+ }
691
+ function normalizedDocText(p) {
692
+ return fs.readFileSync(p, "utf8").replace(/`/g, "");
693
+ }
694
+ export function validateHardGateDocs(root, registry = loadRuntimeRegistry()) {
695
+ const errors = [];
696
+ for (const [relativePath, runtimes] of Object.entries(HARD_GATE_DOC_REQUIREMENTS)) {
697
+ const p = path.join(root, relativePath);
698
+ if (!isFile(p)) {
699
+ errors.push(`${relativePath}: missing hard-gate documentation surface`);
700
+ continue;
701
+ }
702
+ const text = normalizedDocText(p);
703
+ for (const runtime of runtimes) {
704
+ const displayName = runtimeView(registry, runtime).identity.display_name.replace(/ CLI$/, "");
705
+ for (const term of hardGateDocTerms(registry, runtime, relativePath)) {
706
+ const normalizedTerm = term.replace(/`/g, "").replace(/\.$/, "");
707
+ if (!text.includes(normalizedTerm)) {
708
+ errors.push(`${relativePath}: ${displayName} hard-gate docs must keep scoped claim term '${term}'`);
709
+ }
710
+ }
711
+ }
712
+ }
713
+ return errors;
714
+ }
715
+ function setsEqual(a, b) {
716
+ if (a.size !== b.size)
717
+ return false;
718
+ for (const v of a)
719
+ if (!b.has(v))
720
+ return false;
721
+ return true;
722
+ }
723
+ export function lifecycleMain(opts = {}) {
724
+ const root = resolvePath(opts.root ?? rootDefault());
725
+ const out = opts.out ?? ((line) => process.stdout.write(line + "\n"));
726
+ const errors = [];
727
+ let registry = null;
728
+ try {
729
+ registry = loadRuntimeRegistry(path.join(root, "references/adapters/runtime-adapter-registry.yaml"));
730
+ }
731
+ catch (exc) {
732
+ if (exc instanceof RegistryError || exc instanceof Error) {
733
+ errors.push(registryContractError(exc));
734
+ }
735
+ else {
736
+ throw exc;
737
+ }
738
+ }
739
+ if (errors.length > 0) {
740
+ out("lifecycle adapter validation failed:");
741
+ for (const error of errors)
742
+ out(`- ${error}`);
743
+ return 1;
744
+ }
745
+ const reg = registry;
746
+ const copilot = loadJson(path.join(root, "plugin.json"));
747
+ errors.push(...validateCopilot(copilot, root, reg));
748
+ errors.push(...validateCopilotHooks(root, copilot, reg));
749
+ const cursorPlugin = loadJson(path.join(root, ".cursor-plugin/plugin.json"));
750
+ errors.push(...validateCursor(root, cursorPlugin, reg));
751
+ errors.push(...validateCursorHooks(root, reg));
752
+ const codex = loadJson(path.join(root, ".codex-plugin/plugin.json"));
753
+ errors.push(...validateCodex(codex, reg));
754
+ errors.push(...validateCodexProfileraMetadata(root, codex));
755
+ errors.push(...validateOpencode(root, reg));
756
+ const packageManifestReg = packageManifest(root);
757
+ errors.push(...validateSuiteBundleSurface(root, null, packageManifestReg));
758
+ errors.push(...validatePackagedPythonScripts(root));
759
+ if (opts.checkUvRuntime) {
760
+ errors.push(...validateUvRuntime());
761
+ }
762
+ errors.push(...validateHardGateDocs(root, reg));
763
+ if (errors.length > 0) {
764
+ out("lifecycle adapter validation failed:");
765
+ for (const error of errors)
766
+ out(`- ${error}`);
767
+ return 1;
768
+ }
769
+ out("lifecycle adapter metadata ok");
770
+ return 0;
771
+ }
772
+ //# sourceMappingURL=lifecycleAdapters.js.map